1 Premisce

In this Notebook I want to explore methods of phylogenetic imputation, that is, using phylogenetic information to impute missing values in any given dataset, be these continuous (including count) or discrete.

It is important to understand that most methods implicitly rely on the assumption that the phylogeny will help predict the traits better. Either because closely related species tend to share more similar trait values than distantly related species, or because we assume that traits are constrained by evolution according to a certain model (like Brownian Motion). This is of course not always true. Thus is it also important to test for phylogenetic signal in any given trait (more on this later). If traits do not show any phylogenetic signal, then trying to impute them using the phylogeny becomes uninformative and can potentially reduce the precision of the imputed values.

One personal observation is that phylogenetic signal will increase with the size of the dataset. That is, if you are trying to predict traits for a list of 100 species, you might find that using a phylogeny with only 100 tips will perform poorly relative to a phylogeny of 1,000 tips.

It is also possible to incorporate a correlation structure linked to other traits that might be helpful in imputing the missing values, just like you would do if you used a linear model, or imputation by PCA or even Multiple Imputation by Chained Equations (MICE).

Finally, it will be important to have a measure of validation of the imputed values. A cross validation or monte carlo procedure can be used for this (see Molina-Venegas, 2023).

2 Measuring phylogenetic signal

Load the libraries

library(phytools)
library(phangorn)
library(Rphylopars)
library(tidyverse)
library(ape)
library(geiger)
library(missForest)
library(funspace)
library(readxl)
library(car)
library(picante)
library(Hmisc)
library(broom)
library(PerformanceAnalytics)
library(ade4)

Ok, let’s get started with some simple examples.

Lets assume that our dataset consists of continuous and discrete data. Here we can start by using the dataset from Sandra diaz et al., 2022 which is an enhanced dataset containing the 6 major traits of the global spectrum of plant form and function (Diaz et al. 2016) plus some other information for about 46,000 species.

data <- read_xlsx("../Functional traits/Diaz et al. 2022. Enhanced Trait database/Try2023123184331480_Dataset/Dataset/Species_mean_traits.xlsx", sheet = 1, col_names = T) %>% 
  dplyr::rename(Species = `Species name standardized against TPL`, 
                LDMC = `LDMC (g/g)`, 
                LA=`Leaf area (mm2)`, 
                Nmass=`Nmass (mg/g)`, 
                LMA = `LMA (g/m2)`, 
                Height = `Plant height (m)`,
                Seedmass = `Diaspore mass (mg)`,
                SSD = `SSD combined (mg/mm3)`)

data$Species <- gsub(" ", "_", data$Species)

data <- strings2factors(data)

The following character variables were converted to factors
 Taxonomic level Status according to TPL Genus Family Phylogenetic Group within angiosperms Phylogenetic Group General Adaptation to terrestrial or aquatic habitats Woodiness Growth Form Nutrition type (parasitism) Leaf type 
str(data)
tibble [46,047 Ă— 32] (S3: tbl_df/tbl/data.frame)
 $ TRY 30 AccSpecies ID                         : num [1:46047] 89902 2 42370 4 73137 ...
 $ Species                                      : chr [1:46047] "Aaronsohnia_pubescens" "Abarema_adenophora" "Abarema_adenophorum" "Abarema_brachystachya" ...
 $ Taxonomic level                              : Factor w/ 3 levels "genus","species",..: 2 2 2 2 2 2 2 2 2 2 ...
 $ Status according to TPL                      : Factor w/ 5 levels "accepted","invalid name",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ Genus                                        : Factor w/ 7227 levels "Aaronsohnia",..: 1 2 2 2 2 2 2 2 2 2 ...
 $ Family                                       : Factor w/ 419 levels "Acanthaceae",..: 100 214 214 214 214 214 214 214 214 214 ...
 $ Phylogenetic Group within angiosperms        : Factor w/ 4 levels "Angiosperm_Magnoliid",..: 3 3 3 3 3 3 3 3 3 3 ...
 $ Phylogenetic Group General                   : Factor w/ 3 levels "Angiosperm","Gymnosperm",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ Adaptation to terrestrial or aquatic habitats: Factor w/ 4 levels "aquatic","aquatic/semiaquatic",..: 4 4 4 4 4 4 4 4 4 4 ...
 $ Woodiness                                    : Factor w/ 4 levels "non-woody","semi-woody",..: 1 3 3 3 3 3 3 3 3 3 ...
 $ Growth Form                                  : Factor w/ 11 levels "bamboo graminoid",..: 5 11 NA 9 11 11 11 11 11 11 ...
 $ Succulence                                   : logi [1:46047] NA NA NA NA NA NA ...
 $ Nutrition type (parasitism)                  : Factor w/ 4 levels "hemiparasitic",..: 3 3 NA 3 3 3 3 3 3 3 ...
 $ Nutrition type (carnivory)                   : logi [1:46047] NA NA NA NA NA NA ...
 $ Leaf type                                    : Factor w/ 5 levels "broadleaved",..: 1 1 1 1 NA NA 1 1 1 1 ...
 $ LA                                           : num [1:46047] NA 7162 NA NA 700 ...
 $ Leaf area (n.o.)                             : num [1:46047] NA 5 NA NA 1 1 16 NA NA NA ...
 $ Nmass                                        : num [1:46047] NA 24.4 NA 30.4 NA ...
 $ Nmass (n.o.)                                 : num [1:46047] NA 6 NA 1 NA NA 18 2 1 22 ...
 $ LMA                                          : num [1:46047] NA 87.1 NA NA NA ...
 $ LMA (n.o.)                                   : num [1:46047] NA 4 NA NA NA NA 14 2 6 7 ...
 $ Height                                       : num [1:46047] 0.2 NA NA 10 NA ...
 $ Plant height (n.o.)                          : num [1:46047] 2 NA NA 1 NA NA 13 NA 2 7 ...
 $ Seedmass                                     : num [1:46047] NA NA NA NA NA ...
 $ Diaspore mass (n.o.)                         : num [1:46047] NA NA NA NA NA NA 16 NA 7 NA ...
 $ SSD observed (mg/mm3)                        : num [1:46047] NA 0.648 0.36 0.53 NA ...
 $ SSD (n.o.)                                   : num [1:46047] NA 4 2 2 NA NA 29 NA 3 7 ...
 $ LDMC                                         : num [1:46047] NA NA NA NA NA NA NA NA NA NA ...
 $ LDMC (n.o.)                                  : num [1:46047] NA NA NA NA NA NA NA NA NA NA ...
 $ SSD imputed (mg/mm3)                         : num [1:46047] NA NA NA NA NA NA NA NA NA NA ...
 $ SSD                                          : num [1:46047] NA 0.648 0.36 0.53 NA ...
 $ Number of traits with values                 : num [1:46047] 1 4 1 3 1 1 6 2 5 4 ...

We can start by filtering this datset to select only the species which have all observations of traits and are subgeneric level observations.

filtered_data <- data %>% filter(`Number of traits with values` > 5 & `Taxonomic level` != "genus")

dim(filtered_data)
[1] 2214   32

Ok so that’s 2214 species left.

Check if there are any NAs

cat(sum(is.na(filtered_data)) / (nrow(filtered_data)*ncol(filtered_data)) * 100, "%")
15.74498 %

So still about 15% missing data, that is from the column LDMC.

Check the distribution of each variable.

filtered_data %>% dplyr::select(LDMC, LA, Nmass, LMA, SSD, Height, Seedmass) %>% 
  gather %>% 
  ggplot(aes(value, fill=key)) + geom_density() + facet_wrap(facets = ~key, scales = "free")

Now log them all and check they are more or less normally distributed.


new <- filtered_data %>% column_to_rownames("Species") %>% 
  dplyr::select(LDMC, LA, Nmass, LMA, SSD, Height, Seedmass) %>% log() 

new %>%  
  gather %>% 
  ggplot(aes(value, fill=key)) + geom_density() + facet_wrap(facets = ~key, scales = "free")


# Check for normality
apply(new, 2, shapiro.test) %>% do.call(rbind, .)
         statistic p.value      method                        data.name  
LDMC     0.9933977 0.0004499156 "Shapiro-Wilk normality test" "newX[, i]"
LA       0.9829842 1.313264e-15 "Shapiro-Wilk normality test" "newX[, i]"
Nmass    0.9924938 2.978751e-09 "Shapiro-Wilk normality test" "newX[, i]"
LMA      0.9864945 1.262429e-13 "Shapiro-Wilk normality test" "newX[, i]"
SSD      0.9598879 2.850217e-24 "Shapiro-Wilk normality test" "newX[, i]"
Height   0.9232025 3.320459e-32 "Shapiro-Wilk normality test" "newX[, i]"
Seedmass 0.9735828 8.540292e-20 "Shapiro-Wilk normality test" "newX[, i]"

Ok so it’s not perfect but it is much better.

Let’s see what the correlations between traits are.


new %>% 
  as.matrix() %>% 
  Hmisc::rcorr() %>% 
  broom::tidy()

new %>% 
  chart.Correlation()

So there is some good correlation between log(SSD) and log(LDMC) and between log(SSD) and log(Height). log(Height) and log(Seedmass) aren’ too bad either.

Ok now we need a phylogeny for these. Fortunately for us, the package funspace includes a phylogeny for these species Carmona et al. 2021.

phy <- funspace::phylo

2.1 Brownian motion

Most tests for phylogenetic signal rely on Brownian Motion (BM), which is equivalent to a random walk through time with a known variance sigma^2. This means that as time increases, the variance also increases between two given tips.

Two widely used metrics for measuring phylogenetic signal under BM are Pagel’s \(\lambda\) and Bloomberg’s K (see Pagel 1999 and Bloomberg 2003). These are usually defined between [0,1] such as 0 is complete randomness and 1 perfectly matches BM. \(\lambda\) can effectively be higher than 1 but is generally no well defined over that limit. K can be higher than 1, effectively meaning there is stronger signal than you would expect under BM.

For binary categorical traits, the only method I am confident in using is the phylo.d method by Fritz and Purvis (2010), implemented in caper. Another method is the \(\delta\) statistic by Borges et al. 2019 (implemented here https://github.com/mrborges23/delta_statistic/tree/master).

Let’s see if any of our traits can be measured with these two metrics.

As a reminder, I am using log transformed variables for modelling. This is easier to fit under Brownian models of evolution and so is more appropriate to test for phylogenetic signal under these models.

First, let’s match our phylogeny and trait data. Because the dataset is quite large, let’s subsample 500 species and run our analyses on this. We can expand the dataset to see how the tests perform on a larger dataset.

Quick fix in the tree tip labels

phy$tip.label[4884] <- "Cercocarpus_montanus_var._glaber"

Ok now we can compute K and \(\lambda\) for each trait. Some models work best when the trees are completely resolved (ie. no polytomies). Thus we will make sure our tree fits this assumption. If necessary we can transform the tree with polytomies into dichotomies with zero branch lengths using the hand multi2di function.

is.binary(phylo_traits$phy)
[1] FALSE
phylo_traits$phy <- multi2di(phylo_traits$phy)

Great, now let’s calculate K and \(\lambda\) for Leaf Mass per Area.

2.2 LMA


LMA <- phylo_traits$data$LMA %>% setNames(rownames(phylo_traits$data))

(K.LMA<-phylosig(tree = phylo_traits$phy, x = LMA, method = "K", test = T, nsim=500) )

Phylogenetic signal K : 0.0761484 
P-value (based on 500 randomizations) : 0.002 
plot.phylosig(K.LMA)


# This takes a very long time to compute for larger datasets but the geiger function `fitcontinuous` with model = lambda, provides the same result
(lambda.LMA<-phylosig(tree = phylo_traits$phy, x = LMA, method = "lambda", test = T) )

Phylogenetic signal lambda : 0.859225 
logL(lambda) : -378.058 
LR(lambda=0) : 124.506 
P-value (based on LR test) : 6.5287e-29 

Ok, we can see that there is a very low value of K, and that this value is significantly different from random.

But we can also see that there is a much higher value for \(\lambda\).

How can this be? Both are supposed to quantify phylogenetic signal. They do in fact look at different aspects of phylogenetic signal. K is a ratio of within to between clade variance, whilst lambda is a transformation of the diagonal element of the pVCV matrix.

The p-values here compare the results to a null expectation of randomness, but there is not automated way of comparing this to a null expectation of Brownian motion. We have to do this manually.

#p.93 of the book Phylogenetic Comparative Methods in R (Revell & Harmon, 2022)
#simulate 1000 datasets
nullX <- fastBM(phylo_traits$phy, nsim = 1000)
## for each, carry out a test of phylogenetic signal
## amd accumulate these into a vector using apply
nullK <- apply(nullX,2,phylosig, tree=phylo_traits$phy)
#calcultae p.values
Pvals_LMA <- mean(nullK<=K.LMA$K)
Pvals_LMA
[1] 0

Check this out visually

hist(c(nullK, K.LMA$K), breaks=100, col="lightgray", border="black", main="", xlab="K", las=1, cex.axis=0.7, cex.lab=0.9, ylim=c(0,2000), xlim=c(0,4))
arrows(x0=K.LMA$K, y0=par()$usr[4], y1=0, length=0.12, col=make.transparent("blue", 0.5), lwd=2)
text(K.LMA$K, 0.96*par()$usr[4], paste("observed value of K (P=  ", round(Pvals_LMA, 4), ")", sep = ""), pos=4, cex=0.8)

We can see that the distribution of K under Brownian motion is very widespread! But our value of K is indeed lower than expected under Brownian motion.

What about the value of lambda? Can we reject a null of lambda = 1?

We can use a likelihood ratio test for this one.

(LR_LMA <- -2*(lambda.LMA$lik(1) - lambda.LMA$logL) )
[1] 469.5403

Now compute the p-value (assuming chi-square distribution under the null hypothesis of lambda = 1)

Pval_lambda_LMA <- pchisq(LR_LMA, df=1, lower.tail = F )
Pval_lambda_LMA
[1] 4.034683e-104

So we can reject the null that \(\lambda\) = 1.

This means that both K and \(\lambda\) are telling us there is more phylogenetic signal than random, but less than expected under Brownian motion. So perhaps overdispersed ?

We can also compare these results to other models of evolution, like Early-Burst (EB) or Ornstein-Uhlenbeck (OU) models.

(BM.LMA <- fitContinuous(phylo_traits$phy, LMA, model = "BM") )
GEIGER-fitted comparative model of continuous data
 fitted ‘BM’ model parameters:
    sigsq = 0.020824
    z0 = 4.520216

 model summary:
    log-likelihood = -612.828125
    AIC = 1229.656250
    AICc = 1229.680395
    free parameters = 2

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 100
    frequency of best fit = 1.000

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(EB.LMA <- fitContinuous(phylo_traits$phy, LMA, model = "EB") )
GEIGER-fitted comparative model of continuous data
 fitted ‘EB’ model parameters:
    a = -0.000001
    sigsq = 0.020832
    z0 = 4.520218

 model summary:
    log-likelihood = -612.832999
    AIC = 1231.665999
    AICc = 1231.714386
    free parameters = 3

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 1
    frequency of best fit = 0.010

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(OU.LMA <- fitContinuous(phylo_traits$phy, LMA, model = "OU") )
GEIGER-fitted comparative model of continuous data
 fitted ‘OU’ model parameters:
    alpha = 0.145550
    sigsq = 0.105084
    z0 = 4.199499

 model summary:
    log-likelihood = -433.949344
    AIC = 873.898688
    AICc = 873.947075
    free parameters = 3

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 44
    frequency of best fit = 0.440

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(Lambda.LMA <- fitContinuous(phylo_traits$phy, LMA, model = "lambda") )
GEIGER-fitted comparative model of continuous data
 fitted ‘lambda’ model parameters:
    lambda = 0.859240
    sigsq = 0.002478
    z0 = 4.514549

 model summary:
    log-likelihood = -378.057990
    AIC = 762.115979
    AICc = 762.164366
    free parameters = 3

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 16
    frequency of best fit = 0.160

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates

It seems like the a parameter for the EB model is close to 0, which is effectively equivalent to a BM.

If we check the output for the lambda model, this is the same as using the phylosig function from phytools but does it much quicker and with a few more bits of information.

#However if the OU model seems to be at bounds with the `alpha` parameter, then we can increase this and see how the model performs.

#(OU.LMA <- fitContinuous(phylo_traits$phy, LMA, model = "OU", bounds = list(alpha=c(0,50))) )

It’s also good to see how these compare to random noise model. white is a white-noise (non-phylogenetic) model, which assumes data come from a single normal distribution with no covariance structure among species. The variance parameter sigsq takes the same bounds defined under the BM model.

null.LMA <- fitContinuous(phy = phylo_traits$phy, dat = LMA, model = "white")

Now we can compare how well each model performs using AIC

(aics_models <- setNames(c(AIC(BM.LMA), AIC(EB.LMA), AIC(OU.LMA), AIC(null.LMA)), c("BM", "EB", "OU", "NULL")) )
       BM        EB        OU      NULL 
1229.6562 1231.6660  873.8987  884.6218 

Seems like the OU model has the lowest AIC. We can also check the weights.

aic.w(aics_models)
        BM         EB         OU       NULL 
0.00000000 0.00000000 0.99532836 0.00467164 

What about comparing OU to Lambda model ?

aic.w(setNames(c(AIC(OU.LMA), AIC(Lambda.LMA)), c("OU", "Lambda")) )
    OU Lambda 
     0      1 

So in this case the lambda transformation fits the data better.

We can check this out visually and apply the lambda and OU transformations.

#normal tree
contMap(tree = phylo_traits$phy, x = LMA, ftype = "off", type = "fan", lwd=2)


#lambda transformation
#contMap(tree = lambdaTree(phylo_traits$phy,lambda = Lambda.LMA$opt$lambda), x = LMA, fsize = 0.7, ftype = "off", type = "fan", lwd=2)
# or use this
contMap(tree = rescale(phylo_traits$phy, model = "lambda", lambda = Lambda.LMA$opt$lambda), x = LMA, ftype = "off", type = "fan", lwd=2)


#OU transformation
contMap(tree = rescale(phylo_traits$phy, model = "OU", alpha=OU.LMA$opt$alpha), x = LMA, ftype = "off", type = "fan", lwd=2)

The OU model seems quite strange. It looks like most branches have been shortened so much they are almost like a star phylogeny.

We can do this for all the traits one by one and see how much phylogenetic signal they have.

SSD <- phylo_traits$data$SSD %>% setNames(rownames(phylo_traits$data))
Height <- phylo_traits$data$Height %>% setNames(rownames(phylo_traits$data))
LA <- phylo_traits$data$LA%>% setNames(rownames(phylo_traits$data))
LDMC <- phylo_traits$data$LDMC %>% setNames(rownames(phylo_traits$data)) # has some NA values
Nmass <- phylo_traits$data$Nmass %>% setNames(rownames(phylo_traits$data))
Seedmass <- phylo_traits$data$Seedmass %>% setNames(rownames(phylo_traits$data))

Now fit the models

2.3 SSD

(K.SSD<-phylosig(tree = phylo_traits$phy, x = SSD, method = "K", test = T, nsim=500) )

Phylogenetic signal K : 0.134274 
P-value (based on 500 randomizations) : 0.002 
plot.phylosig(K.SSD)


(Lambda.SSD <- fitContinuous(phylo_traits$phy, SSD, model = "lambda") )
GEIGER-fitted comparative model of continuous data
 fitted ‘lambda’ model parameters:
    lambda = 0.957333
    sigsq = 0.002486
    z0 = -1.245813

 model summary:
    log-likelihood = -242.942713
    AIC = 491.885426
    AICc = 491.933814
    free parameters = 3

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 1
    frequency of best fit = 0.010

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(BM.SSD <- fitContinuous(phylo_traits$phy, SSD, model = "BM") )
GEIGER-fitted comparative model of continuous data
 fitted ‘BM’ model parameters:
    sigsq = 0.008738
    z0 = -1.250112

 model summary:
    log-likelihood = -395.732158
    AIC = 795.464317
    AICc = 795.488462
    free parameters = 2

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 100
    frequency of best fit = 1.000

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(EB.SSD <- fitContinuous(phylo_traits$phy, SSD, model = "EB") )
GEIGER-fitted comparative model of continuous data
 fitted ‘EB’ model parameters:
    a = -0.000001
    sigsq = 0.008742
    z0 = -1.250115

 model summary:
    log-likelihood = -395.736140
    AIC = 797.472280
    AICc = 797.520667
    free parameters = 3

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 1
    frequency of best fit = 0.010

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(OU.SSD <- fitContinuous(phylo_traits$phy, SSD, model = "OU") )
GEIGER-fitted comparative model of continuous data
 fitted ‘OU’ model parameters:
    alpha = 0.019912
    sigsq = 0.014408
    z0 = -1.096850

 model summary:
    log-likelihood = -328.794977
    AIC = 663.589953
    AICc = 663.638340
    free parameters = 3

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 19
    frequency of best fit = 0.190

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(null.SSD <- fitContinuous(phylo_traits$phy, SSD, model = "white"))
GEIGER-fitted comparative model of continuous data
 fitted ‘white’ model parameters:
    sigsq = 0.320221
    z0 = -1.124992

 model summary:
    log-likelihood = -424.783457
    AIC = 853.566915
    AICc = 853.591060
    free parameters = 2

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 100
    frequency of best fit = 1.000

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates

Here \(\lambda\) is 0.96, so pretty strong

Check which fits best and the AIC weights

(aics_models <- setNames(c(AIC(BM.SSD), AIC(EB.SSD), AIC(OU.SSD), AIC(null.SSD)), c("BM", "EB", "OU", "NULL")) )
      BM       EB       OU     NULL 
795.4643 797.4723 663.5900 853.5669 
aic.w(aics_models)
  BM   EB   OU NULL 
   0    0    1    0 

Again, OU seems to do best.

2.4 Nmass

(K.Nmass<-phylosig(tree = phylo_traits$phy, x = Nmass, method = "K", test = T, nsim=500) )

Phylogenetic signal K : 0.0108426 
P-value (based on 500 randomizations) : 0.554 
plot.phylosig(K.Nmass)


(Lambda.Nmass <- fitContinuous(phylo_traits$phy, Nmass, model = "lambda") )
GEIGER-fitted comparative model of continuous data
 fitted ‘lambda’ model parameters:
    lambda = 0.800613
    sigsq = 0.000846
    z0 = 2.849721

 model summary:
    log-likelihood = -159.493136
    AIC = 324.986273
    AICc = 325.034660
    free parameters = 3

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 14
    frequency of best fit = 0.140

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(BM.Nmass <- fitContinuous(phylo_traits$phy, Nmass, model = "BM") )
GEIGER-fitted comparative model of continuous data
 fitted ‘BM’ model parameters:
    sigsq = 0.058183
    z0 = 2.853405

 model summary:
    log-likelihood = -869.700663
    AIC = 1743.401326
    AICc = 1743.425471
    free parameters = 2

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 100
    frequency of best fit = 1.000

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(EB.Nmass <- fitContinuous(phylo_traits$phy, Nmass, model = "EB") )
GEIGER-fitted comparative model of continuous data
 fitted ‘EB’ model parameters:
    a = -0.000001
    sigsq = 0.058206
    z0 = 2.853405

 model summary:
    log-likelihood = -869.706608
    AIC = 1745.413216
    AICc = 1745.461603
    free parameters = 3

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 1
    frequency of best fit = 0.010

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(OU.Nmass <- fitContinuous(phylo_traits$phy, Nmass, model = "OU") )
Warning: 
Parameter estimates appear at bounds:
    alpha
GEIGER-fitted comparative model of continuous data
 fitted ‘OU’ model parameters:
    alpha = 2.718282
    sigsq = 0.861545
    z0 = 3.027972

 model summary:
    log-likelihood = -247.247537
    AIC = 500.495075
    AICc = 500.543462
    free parameters = 3

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 38
    frequency of best fit = 0.380

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(null.Nmass <- fitContinuous(phylo_traits$phy, Nmass, model = "white"))
GEIGER-fitted comparative model of continuous data
 fitted ‘white’ model parameters:
    sigsq = 0.149826
    z0 = 3.028794

 model summary:
    log-likelihood = -234.898773
    AIC = 473.797546
    AICc = 473.821691
    free parameters = 2

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 100
    frequency of best fit = 1.000

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates

Here K seems to be non-significant. Lambda is moderate to high.

Check AIC weights

(aics_models <- setNames(c(AIC(BM.Nmass), AIC(EB.Nmass), AIC(OU.Nmass), AIC(null.Nmass)), c("BM", "EB", "OU", "NULL")) )
       BM        EB        OU      NULL 
1743.4013 1745.4132  500.4951  473.7975 
aic.w(aics_models)
        BM         EB         OU       NULL 
0.00000000 0.00000000 0.00000159 0.99999841 

This time a null model of white noise (grand mean) is best.

2.5 Height

(K.Height<-phylosig(tree = phylo_traits$phy, x = Height, method = "K", test = T, nsim=500) )

Phylogenetic signal K : 0.198507 
P-value (based on 500 randomizations) : 0.002 
plot.phylosig(K.Height)


(Lambda.Height <- fitContinuous(phylo_traits$phy, Height, model = "lambda") )
GEIGER-fitted comparative model of continuous data
 fitted ‘lambda’ model parameters:
    lambda = 0.982437
    sigsq = 0.033418
    z0 = 0.807796

 model summary:
    log-likelihood = -826.258043
    AIC = 1658.516086
    AICc = 1658.564473
    free parameters = 3

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 12
    frequency of best fit = 0.120

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(BM.Height <- fitContinuous(phylo_traits$phy, Height, model = "BM") )
GEIGER-fitted comparative model of continuous data
 fitted ‘BM’ model parameters:
    sigsq = 0.079894
    z0 = 0.807071

 model summary:
    log-likelihood = -948.979485
    AIC = 1901.958971
    AICc = 1901.983116
    free parameters = 2

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 100
    frequency of best fit = 1.000

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(EB.Height <- fitContinuous(phylo_traits$phy, Height, model = "EB") )
GEIGER-fitted comparative model of continuous data
 fitted ‘EB’ model parameters:
    a = -0.000001
    sigsq = 0.079927
    z0 = 0.807065

 model summary:
    log-likelihood = -948.982876
    AIC = 1903.965752
    AICc = 1904.014139
    free parameters = 3

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 1
    frequency of best fit = 0.010

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(OU.Height <- fitContinuous(phylo_traits$phy, Height, model = "OU") )
GEIGER-fitted comparative model of continuous data
 fitted ‘OU’ model parameters:
    alpha = 0.012628
    sigsq = 0.115850
    z0 = 0.837843

 model summary:
    log-likelihood = -908.809345
    AIC = 1823.618690
    AICc = 1823.667077
    free parameters = 3

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 9
    frequency of best fit = 0.090

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(null.Height <- fitContinuous(phylo_traits$phy, Height, model = "white"))
GEIGER-fitted comparative model of continuous data
 fitted ‘white’ model parameters:
    sigsq = 4.373830
    z0 = 0.399501

 model summary:
    log-likelihood = -1078.379066
    AIC = 2160.758132
    AICc = 2160.782277
    free parameters = 2

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 100
    frequency of best fit = 1.000

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates

Lambda is pretty much equal to 1

Check AIC weights

(aics_models <- setNames(c(AIC(BM.Height), AIC(EB.Height), AIC(OU.Height), AIC(null.Height)), c("BM", "EB", "OU", "NULL")) )
      BM       EB       OU     NULL 
1901.959 1903.966 1823.619 2160.758 
aic.w(aics_models)
  BM   EB   OU NULL 
   0    0    1    0 

OU model fits data best

2.6 LA

(K.LA<-phylosig(tree = phylo_traits$phy, x = LA, method = "K", test = T, nsim=500) )

Phylogenetic signal K : 0.128972 
P-value (based on 500 randomizations) : 0.002 
plot.phylosig(K.LA)


(Lambda.LA <- fitContinuous(phylo_traits$phy, LA, model = "lambda") )
GEIGER-fitted comparative model of continuous data
 fitted ‘lambda’ model parameters:
    lambda = 0.875074
    sigsq = 0.031594
    z0 = 5.328225

 model summary:
    log-likelihood = -998.321917
    AIC = 2002.643833
    AICc = 2002.692220
    free parameters = 3

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 22
    frequency of best fit = 0.220

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(BM.LA <- fitContinuous(phylo_traits$phy, LA, model = "BM") )
GEIGER-fitted comparative model of continuous data
 fitted ‘BM’ model parameters:
    sigsq = 0.171152
    z0 = 5.340679

 model summary:
    log-likelihood = -1139.441718
    AIC = 2282.883436
    AICc = 2282.907581
    free parameters = 2

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 100
    frequency of best fit = 1.000

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(EB.LA <- fitContinuous(phylo_traits$phy, LA, model = "EB") )
GEIGER-fitted comparative model of continuous data
 fitted ‘EB’ model parameters:
    a = -0.000001
    sigsq = 0.171218
    z0 = 5.340664

 model summary:
    log-likelihood = -1139.446085
    AIC = 2284.892170
    AICc = 2284.940557
    free parameters = 3

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 13
    frequency of best fit = 0.130

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(OU.LA <- fitContinuous(phylo_traits$phy, LA, model = "OU") )
GEIGER-fitted comparative model of continuous data
 fitted ‘OU’ model parameters:
    alpha = 0.040244
    sigsq = 0.388378
    z0 = 6.811381

 model summary:
    log-likelihood = -1033.938724
    AIC = 2073.877447
    AICc = 2073.925834
    free parameters = 3

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 20
    frequency of best fit = 0.200

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(null.LA <- fitContinuous(phylo_traits$phy, LA, model = "white"))
GEIGER-fitted comparative model of continuous data
 fitted ‘white’ model parameters:
    sigsq = 4.365239
    z0 = 6.738389

 model summary:
    log-likelihood = -1077.887512
    AIC = 2159.775023
    AICc = 2159.799168
    free parameters = 2

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 100
    frequency of best fit = 1.000

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates

Check AIC weights

(aics_models <- setNames(c(AIC(BM.LA), AIC(EB.LA), AIC(OU.LA), AIC(null.LA)), c("BM", "EB", "OU", "NULL")) )
      BM       EB       OU     NULL 
2282.883 2284.892 2073.877 2159.775 
aic.w(aics_models)
  BM   EB   OU NULL 
   0    0    1    0 

Again, OU

2.7 LDMC

(K.LDMC<-phylosig(tree = phylo_traits$phy, x = LDMC[!is.na(LDMC)], method = "K", test = T, nsim=500) )
[1] "some species in tree are missing from x , dropping missing taxa from the tree"

Phylogenetic signal K : 0.216002 
P-value (based on 500 randomizations) : 0.002 
plot.phylosig(K.LDMC)


(Lambda.LDMC <- fitContinuous(phylo_traits$phy, LDMC[!is.na(LDMC)], model = "lambda") )
Warning: The following tips were not found in 'data' and were dropped from 'phy':
    Abies_grandis
    Acacia_aroma
    Acacia_auriculiformis
    Acacia_doratoxylon
    Acacia_karroo
    Acacia_mearnsii
    Acacia_praecox
    Acer_monspessulanum
    Acer_saccharinum
    Acer_saccharum
    Albizia_saman
    Alliaria_petiolata
    Alnus_rhombifolia
    Ambrosia_psilostachya
    Anagallis_tenella
    Andira_inermis
    Anemone_nemorosa
    Annona_spraguei
    Apeiba_tibourbou
    Araucaria_araucana
    Arenaria_serpyllifolia
    Artemisia_californica
    Artemisia_dracunculus
    Aspidosperma_album
    Banksia_marginata
    Betula_papyrifera
    Betula_platyphylla
    Bombax_ceiba
    Briza_media
    Bromus_hordeaceus
    Bromus_sterilis
    Brosimum_utile
    Brownea_grandiceps
    Butea_monosperma
    Byrsonima_crassifolia
    Calamagrostis_epigejos
    Calophyllum_longifolium
    Campanula_rotundifolia
    Capsella_bursa-pastoris
    Carex_binervis
    Carex_hirta
    Carex_pallescens
    Carex_panicea
    Carya_glabra
    Caryocar_glabrum
    Casearia_sylvestris
    Castanea_dentata
    Ceanothus_spinosus
    Cecropia_obtusifolia
    Cecropia_peltata
    Ceiba_speciosa
    Cercocarpus_montanus_var._glaber
    Cinnamomum_subavenium
    Cirsium_wheeleri
    Cojoba_rufescens
    Colophospermum_mopane
    Cordia_bicolor
    Cordia_caffra
    Cordia_megalantha
    Cornus_glabrata
    Cornus_sericea
    Crepis_pyrenaica
    Croton_capitatus
    Cupania_rufescens
    Cupressus_sempervirens
    Cussonia_spicata
    Cytisus_scoparius
    Dacrydium_cupressinum
    Danthonia_decumbens
    Daphniphyllum_pentandrum
    Drimys_winteri
    Dryandra_sessilis
    Drypetes_standleyi
    Duguetia_quitarensis
    Ekebergia_capensis
    Elaeagnus_rhamnoides
    Emmotum_fagifolium
    Enterolobium_schomburgkii
    Epilobium_palustre
    Equisetum_arvense
    Equisetum_fluviatile
    Equisetum_palustre
    Erica_tetralix
    Erythrophleum_chlorostachys
    Eschweilera_decolorans
    Eschweilera_parviflora
    Eschweilera_sagotiana
    Eucalyptus_acmenoides
    Eucalyptus_grandis
    Eucalyptus_umbra
    Euphorbia_brachycera
    Euphorbia_corollata
    Faramea_occidentalis
    Fragaria_vesca
    Frangula_californica
    Fraxinus_uhdei
    Galium_verum
    Garcinia_macrophylla
    Grewia_monticola
    Griselinia_littoralis
    Guatteria_dumetorum
    Gymnosporia_heterophylla
    Hakea_cygna
    Hakea_preissii
    Hamamelis_virginiana
    Hampea_appendiculata
    Hampea_nutricia
    Heliocarpus_appendiculatus
    Heracleum_sphondylium
    Heteromeles_arbutifolia
    Holcus_mollis
    Holodiscus_discolor
    Ilex_mitis
    Inga_acreana
    Inga_goldmanii
    Inga_nobilis
    Inga_sapindoides
    Iris_missouriensis
    Iryanthera_juruensis
    Jacaranda_copaia
    Jacaratia_digitata
    Jacobaea_vulgaris
    Juncus_articulatus
    Juniperus_communis
    Kigelia_africana
    Koeleria_macrantha
    Lacmellea_panamensis
    Lactuca_muralis
    Laetia_corymbulosa
    Larix_kaempferi
    Larrea_cuneifolia
    Larrea_divaricata
    Lathyrus_pratensis
    Lecythis_zabucajo
    Leiospermum_racemosum
    Lepechinia_calycina
    Leptospermum_polygalifolium
    Leucadendron_salignum
    Ligustrum_lucidum
    Liquidambar_styraciflua
    Lotus_pedunculatus
    Lysimachia_vulgaris
    Lythrum_salicaria
    Macaranga_hypoleuca
    Machilus_chinensis
    Macrolobium_gracile
    Magnolia_grandiflora
    Malus_sylvestris
    Mangifera_indica
    Margaritaria_nobilis
    Melaleuca_leucadendra
    Metrosideros_polymorpha
    Mimulus_aurantiacus
    Molinia_caerulea
    Mosannona_garwoodii
    Murraya_paniculata
    Myrcia_splendens
    Nothofagus_betuloides
    Nothofagus_dombeyi
    Nothofagus_fusca
    Nothofagus_solandri
    Ochroma_pyramidale
    Ocotea_floribunda
    Ocotea_puberula
    Opuntia_sulphurea
    Ormosia_macrocalyx
    Orthion_oblanceolatum
    Ouratea_lucens
    Oxalis_acetosella
    Pachira_aquatica
    Pachira_insignis
    Packera_multilobata
    Palicourea_tetragona
    Parinari_campestris
    Parkinsonia_praecox
    Parnassia_palustris
    Peltogyne_venosa
    Perebea_xanthochyma
    Petrea_volubilis
    Picea_sitchensis
    Picramnia_latifolia
    Pimpinella_major
    Pinus_clausa
    Pinus_koraiensis
    Pinus_massoniana
    Piper_aequale
    Piscidia_carthagenensis
    Pistacia_lentiscus
    Pongamia_pinnata
    Populus_nigra
    Populus_tremuloides
    Pourouma_bicolor
    Pouteria_cladantha
    Pouteria_ephedrantha
    Pradosia_cochlearia
    Prosopis_flexuosa
    Prosopis_torquata
    Protium_tenuifolium
    Prunus_mahaleb
    Prunus_pensylvanica
    Pseudocymopterus_montanus
    Psidium_guajava
    Psychotria_marginata
    Pterocarpus_rohrii
    Pterygota_alata
    Quararibea_asterolepis
    Quassia_amara
    Quercus_lusitanica
    Quercus_robur
    Quercus_shumardii
    Quercus_stellata
    Quercus_variabilis
    Ranunculus_bulbosus
    Reynoutria_japonica
    Rhamnus_alaternus
    Rollinia_pittieri
    Rosa_woodsii
    Rubus_parviflorus
    Ruellia_humilis
    Rumex_obtusifolius
    Salix_cinerea
    Salix_lasiolepis
    Salix_nigra
    Schinopsis_marginata
    Schizolobium_parahyba
    Sequoia_sempervirens
    Shorea_leprosula
    Shorea_macroptera
    Shorea_parvifolia
    Shorea_smithiana
    Silphium_terebinthinaceum
    Sloanea_guianensis
    Sloanea_terniflora
    Solanum_dulcamara
    Sorbus_aria
    Sorocea_steinbachii
    Spondias_mombin
    Spondias_pinnata
    Sporobolus_interruptus
    Sterculia_speciosa
    Stipa_comata
    Tabernaemontana_arborea
    Tabernaemontana_donnell-smithii
    Tachigali_versicolor
    Talisia_princeps
    Tanacetum_corymbosum
    Tapirira_obtusa
    Taraxacum_campylodes
    Taxodium_distichum
    Taxus_baccata
    Terminalia_amazonia
    Theobroma_cacao
    Theobroma_speciosum
    Thevetia_ahouai
    Trattinnickia_aspera
    Triadica_cochinchinensis
    Trichilia_pleeana
    Trichilia_quadrijuga
    Trichospermum_mexicanum
    Trifolium_dubium
    Triplaris_cumingiana
    Ulmus_glabra
    Ulmus_minor
    Ulmus_pumila
    Ulmus_rubra
    Unonopsis_rufescens
    Vaccinium_myrtillus
    Vaccinium_vitis-idaea
    Verbena_bracteata
    Veronica_chamaedrys
    Viburnum_opulus
    Victoria_amazonica
    Viola_canina
    Vismia_baccifera
    Vitex_negundo
    Vitex_pinnata
    Ziziphus_mucronata
    Zuelania_guidonia
GEIGER-fitted comparative model of continuous data
 fitted ‘lambda’ model parameters:
    lambda = 0.740265
    sigsq = 0.001569
    z0 = -1.707557

 model summary:
    log-likelihood = -80.434508
    AIC = 166.869016
    AICc = 166.979108
    free parameters = 3

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 20
    frequency of best fit = 0.200

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(BM.LDMC <- fitContinuous(phylo_traits$phy, LDMC[!is.na(LDMC)], model = "BM") )
Warning: The following tips were not found in 'data' and were dropped from 'phy':
    Abies_grandis
    Acacia_aroma
    Acacia_auriculiformis
    Acacia_doratoxylon
    Acacia_karroo
    Acacia_mearnsii
    Acacia_praecox
    Acer_monspessulanum
    Acer_saccharinum
    Acer_saccharum
    Albizia_saman
    Alliaria_petiolata
    Alnus_rhombifolia
    Ambrosia_psilostachya
    Anagallis_tenella
    Andira_inermis
    Anemone_nemorosa
    Annona_spraguei
    Apeiba_tibourbou
    Araucaria_araucana
    Arenaria_serpyllifolia
    Artemisia_californica
    Artemisia_dracunculus
    Aspidosperma_album
    Banksia_marginata
    Betula_papyrifera
    Betula_platyphylla
    Bombax_ceiba
    Briza_media
    Bromus_hordeaceus
    Bromus_sterilis
    Brosimum_utile
    Brownea_grandiceps
    Butea_monosperma
    Byrsonima_crassifolia
    Calamagrostis_epigejos
    Calophyllum_longifolium
    Campanula_rotundifolia
    Capsella_bursa-pastoris
    Carex_binervis
    Carex_hirta
    Carex_pallescens
    Carex_panicea
    Carya_glabra
    Caryocar_glabrum
    Casearia_sylvestris
    Castanea_dentata
    Ceanothus_spinosus
    Cecropia_obtusifolia
    Cecropia_peltata
    Ceiba_speciosa
    Cercocarpus_montanus_var._glaber
    Cinnamomum_subavenium
    Cirsium_wheeleri
    Cojoba_rufescens
    Colophospermum_mopane
    Cordia_bicolor
    Cordia_caffra
    Cordia_megalantha
    Cornus_glabrata
    Cornus_sericea
    Crepis_pyrenaica
    Croton_capitatus
    Cupania_rufescens
    Cupressus_sempervirens
    Cussonia_spicata
    Cytisus_scoparius
    Dacrydium_cupressinum
    Danthonia_decumbens
    Daphniphyllum_pentandrum
    Drimys_winteri
    Dryandra_sessilis
    Drypetes_standleyi
    Duguetia_quitarensis
    Ekebergia_capensis
    Elaeagnus_rhamnoides
    Emmotum_fagifolium
    Enterolobium_schomburgkii
    Epilobium_palustre
    Equisetum_arvense
    Equisetum_fluviatile
    Equisetum_palustre
    Erica_tetralix
    Erythrophleum_chlorostachys
    Eschweilera_decolorans
    Eschweilera_parviflora
    Eschweilera_sagotiana
    Eucalyptus_acmenoides
    Eucalyptus_grandis
    Eucalyptus_umbra
    Euphorbia_brachycera
    Euphorbia_corollata
    Faramea_occidentalis
    Fragaria_vesca
    Frangula_californica
    Fraxinus_uhdei
    Galium_verum
    Garcinia_macrophylla
    Grewia_monticola
    Griselinia_littoralis
    Guatteria_dumetorum
    Gymnosporia_heterophylla
    Hakea_cygna
    Hakea_preissii
    Hamamelis_virginiana
    Hampea_appendiculata
    Hampea_nutricia
    Heliocarpus_appendiculatus
    Heracleum_sphondylium
    Heteromeles_arbutifolia
    Holcus_mollis
    Holodiscus_discolor
    Ilex_mitis
    Inga_acreana
    Inga_goldmanii
    Inga_nobilis
    Inga_sapindoides
    Iris_missouriensis
    Iryanthera_juruensis
    Jacaranda_copaia
    Jacaratia_digitata
    Jacobaea_vulgaris
    Juncus_articulatus
    Juniperus_communis
    Kigelia_africana
    Koeleria_macrantha
    Lacmellea_panamensis
    Lactuca_muralis
    Laetia_corymbulosa
    Larix_kaempferi
    Larrea_cuneifolia
    Larrea_divaricata
    Lathyrus_pratensis
    Lecythis_zabucajo
    Leiospermum_racemosum
    Lepechinia_calycina
    Leptospermum_polygalifolium
    Leucadendron_salignum
    Ligustrum_lucidum
    Liquidambar_styraciflua
    Lotus_pedunculatus
    Lysimachia_vulgaris
    Lythrum_salicaria
    Macaranga_hypoleuca
    Machilus_chinensis
    Macrolobium_gracile
    Magnolia_grandiflora
    Malus_sylvestris
    Mangifera_indica
    Margaritaria_nobilis
    Melaleuca_leucadendra
    Metrosideros_polymorpha
    Mimulus_aurantiacus
    Molinia_caerulea
    Mosannona_garwoodii
    Murraya_paniculata
    Myrcia_splendens
    Nothofagus_betuloides
    Nothofagus_dombeyi
    Nothofagus_fusca
    Nothofagus_solandri
    Ochroma_pyramidale
    Ocotea_floribunda
    Ocotea_puberula
    Opuntia_sulphurea
    Ormosia_macrocalyx
    Orthion_oblanceolatum
    Ouratea_lucens
    Oxalis_acetosella
    Pachira_aquatica
    Pachira_insignis
    Packera_multilobata
    Palicourea_tetragona
    Parinari_campestris
    Parkinsonia_praecox
    Parnassia_palustris
    Peltogyne_venosa
    Perebea_xanthochyma
    Petrea_volubilis
    Picea_sitchensis
    Picramnia_latifolia
    Pimpinella_major
    Pinus_clausa
    Pinus_koraiensis
    Pinus_massoniana
    Piper_aequale
    Piscidia_carthagenensis
    Pistacia_lentiscus
    Pongamia_pinnata
    Populus_nigra
    Populus_tremuloides
    Pourouma_bicolor
    Pouteria_cladantha
    Pouteria_ephedrantha
    Pradosia_cochlearia
    Prosopis_flexuosa
    Prosopis_torquata
    Protium_tenuifolium
    Prunus_mahaleb
    Prunus_pensylvanica
    Pseudocymopterus_montanus
    Psidium_guajava
    Psychotria_marginata
    Pterocarpus_rohrii
    Pterygota_alata
    Quararibea_asterolepis
    Quassia_amara
    Quercus_lusitanica
    Quercus_robur
    Quercus_shumardii
    Quercus_stellata
    Quercus_variabilis
    Ranunculus_bulbosus
    Reynoutria_japonica
    Rhamnus_alaternus
    Rollinia_pittieri
    Rosa_woodsii
    Rubus_parviflorus
    Ruellia_humilis
    Rumex_obtusifolius
    Salix_cinerea
    Salix_lasiolepis
    Salix_nigra
    Schinopsis_marginata
    Schizolobium_parahyba
    Sequoia_sempervirens
    Shorea_leprosula
    Shorea_macroptera
    Shorea_parvifolia
    Shorea_smithiana
    Silphium_terebinthinaceum
    Sloanea_guianensis
    Sloanea_terniflora
    Solanum_dulcamara
    Sorbus_aria
    Sorocea_steinbachii
    Spondias_mombin
    Spondias_pinnata
    Sporobolus_interruptus
    Sterculia_speciosa
    Stipa_comata
    Tabernaemontana_arborea
    Tabernaemontana_donnell-smithii
    Tachigali_versicolor
    Talisia_princeps
    Tanacetum_corymbosum
    Tapirira_obtusa
    Taraxacum_campylodes
    Taxodium_distichum
    Taxus_baccata
    Terminalia_amazonia
    Theobroma_cacao
    Theobroma_speciosum
    Thevetia_ahouai
    Trattinnickia_aspera
    Triadica_cochinchinensis
    Trichilia_pleeana
    Trichilia_quadrijuga
    Trichospermum_mexicanum
    Trifolium_dubium
    Triplaris_cumingiana
    Ulmus_glabra
    Ulmus_minor
    Ulmus_pumila
    Ulmus_rubra
    Unonopsis_rufescens
    Vaccinium_myrtillus
    Vaccinium_vitis-idaea
    Verbena_bracteata
    Veronica_chamaedrys
    Viburnum_opulus
    Victoria_amazonica
    Viola_canina
    Vismia_baccifera
    Vitex_negundo
    Vitex_pinnata
    Ziziphus_mucronata
    Zuelania_guidonia
GEIGER-fitted comparative model of continuous data
 fitted ‘BM’ model parameters:
    sigsq = 0.007245
    z0 = -1.725638

 model summary:
    log-likelihood = -147.272308
    AIC = 298.544617
    AICc = 298.599411
    free parameters = 2

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 100
    frequency of best fit = 1.000

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(EB.LDMC <- fitContinuous(phylo_traits$phy, LDMC[!is.na(LDMC)], model = "EB") )
Warning: The following tips were not found in 'data' and were dropped from 'phy':
    Abies_grandis
    Acacia_aroma
    Acacia_auriculiformis
    Acacia_doratoxylon
    Acacia_karroo
    Acacia_mearnsii
    Acacia_praecox
    Acer_monspessulanum
    Acer_saccharinum
    Acer_saccharum
    Albizia_saman
    Alliaria_petiolata
    Alnus_rhombifolia
    Ambrosia_psilostachya
    Anagallis_tenella
    Andira_inermis
    Anemone_nemorosa
    Annona_spraguei
    Apeiba_tibourbou
    Araucaria_araucana
    Arenaria_serpyllifolia
    Artemisia_californica
    Artemisia_dracunculus
    Aspidosperma_album
    Banksia_marginata
    Betula_papyrifera
    Betula_platyphylla
    Bombax_ceiba
    Briza_media
    Bromus_hordeaceus
    Bromus_sterilis
    Brosimum_utile
    Brownea_grandiceps
    Butea_monosperma
    Byrsonima_crassifolia
    Calamagrostis_epigejos
    Calophyllum_longifolium
    Campanula_rotundifolia
    Capsella_bursa-pastoris
    Carex_binervis
    Carex_hirta
    Carex_pallescens
    Carex_panicea
    Carya_glabra
    Caryocar_glabrum
    Casearia_sylvestris
    Castanea_dentata
    Ceanothus_spinosus
    Cecropia_obtusifolia
    Cecropia_peltata
    Ceiba_speciosa
    Cercocarpus_montanus_var._glaber
    Cinnamomum_subavenium
    Cirsium_wheeleri
    Cojoba_rufescens
    Colophospermum_mopane
    Cordia_bicolor
    Cordia_caffra
    Cordia_megalantha
    Cornus_glabrata
    Cornus_sericea
    Crepis_pyrenaica
    Croton_capitatus
    Cupania_rufescens
    Cupressus_sempervirens
    Cussonia_spicata
    Cytisus_scoparius
    Dacrydium_cupressinum
    Danthonia_decumbens
    Daphniphyllum_pentandrum
    Drimys_winteri
    Dryandra_sessilis
    Drypetes_standleyi
    Duguetia_quitarensis
    Ekebergia_capensis
    Elaeagnus_rhamnoides
    Emmotum_fagifolium
    Enterolobium_schomburgkii
    Epilobium_palustre
    Equisetum_arvense
    Equisetum_fluviatile
    Equisetum_palustre
    Erica_tetralix
    Erythrophleum_chlorostachys
    Eschweilera_decolorans
    Eschweilera_parviflora
    Eschweilera_sagotiana
    Eucalyptus_acmenoides
    Eucalyptus_grandis
    Eucalyptus_umbra
    Euphorbia_brachycera
    Euphorbia_corollata
    Faramea_occidentalis
    Fragaria_vesca
    Frangula_californica
    Fraxinus_uhdei
    Galium_verum
    Garcinia_macrophylla
    Grewia_monticola
    Griselinia_littoralis
    Guatteria_dumetorum
    Gymnosporia_heterophylla
    Hakea_cygna
    Hakea_preissii
    Hamamelis_virginiana
    Hampea_appendiculata
    Hampea_nutricia
    Heliocarpus_appendiculatus
    Heracleum_sphondylium
    Heteromeles_arbutifolia
    Holcus_mollis
    Holodiscus_discolor
    Ilex_mitis
    Inga_acreana
    Inga_goldmanii
    Inga_nobilis
    Inga_sapindoides
    Iris_missouriensis
    Iryanthera_juruensis
    Jacaranda_copaia
    Jacaratia_digitata
    Jacobaea_vulgaris
    Juncus_articulatus
    Juniperus_communis
    Kigelia_africana
    Koeleria_macrantha
    Lacmellea_panamensis
    Lactuca_muralis
    Laetia_corymbulosa
    Larix_kaempferi
    Larrea_cuneifolia
    Larrea_divaricata
    Lathyrus_pratensis
    Lecythis_zabucajo
    Leiospermum_racemosum
    Lepechinia_calycina
    Leptospermum_polygalifolium
    Leucadendron_salignum
    Ligustrum_lucidum
    Liquidambar_styraciflua
    Lotus_pedunculatus
    Lysimachia_vulgaris
    Lythrum_salicaria
    Macaranga_hypoleuca
    Machilus_chinensis
    Macrolobium_gracile
    Magnolia_grandiflora
    Malus_sylvestris
    Mangifera_indica
    Margaritaria_nobilis
    Melaleuca_leucadendra
    Metrosideros_polymorpha
    Mimulus_aurantiacus
    Molinia_caerulea
    Mosannona_garwoodii
    Murraya_paniculata
    Myrcia_splendens
    Nothofagus_betuloides
    Nothofagus_dombeyi
    Nothofagus_fusca
    Nothofagus_solandri
    Ochroma_pyramidale
    Ocotea_floribunda
    Ocotea_puberula
    Opuntia_sulphurea
    Ormosia_macrocalyx
    Orthion_oblanceolatum
    Ouratea_lucens
    Oxalis_acetosella
    Pachira_aquatica
    Pachira_insignis
    Packera_multilobata
    Palicourea_tetragona
    Parinari_campestris
    Parkinsonia_praecox
    Parnassia_palustris
    Peltogyne_venosa
    Perebea_xanthochyma
    Petrea_volubilis
    Picea_sitchensis
    Picramnia_latifolia
    Pimpinella_major
    Pinus_clausa
    Pinus_koraiensis
    Pinus_massoniana
    Piper_aequale
    Piscidia_carthagenensis
    Pistacia_lentiscus
    Pongamia_pinnata
    Populus_nigra
    Populus_tremuloides
    Pourouma_bicolor
    Pouteria_cladantha
    Pouteria_ephedrantha
    Pradosia_cochlearia
    Prosopis_flexuosa
    Prosopis_torquata
    Protium_tenuifolium
    Prunus_mahaleb
    Prunus_pensylvanica
    Pseudocymopterus_montanus
    Psidium_guajava
    Psychotria_marginata
    Pterocarpus_rohrii
    Pterygota_alata
    Quararibea_asterolepis
    Quassia_amara
    Quercus_lusitanica
    Quercus_robur
    Quercus_shumardii
    Quercus_stellata
    Quercus_variabilis
    Ranunculus_bulbosus
    Reynoutria_japonica
    Rhamnus_alaternus
    Rollinia_pittieri
    Rosa_woodsii
    Rubus_parviflorus
    Ruellia_humilis
    Rumex_obtusifolius
    Salix_cinerea
    Salix_lasiolepis
    Salix_nigra
    Schinopsis_marginata
    Schizolobium_parahyba
    Sequoia_sempervirens
    Shorea_leprosula
    Shorea_macroptera
    Shorea_parvifolia
    Shorea_smithiana
    Silphium_terebinthinaceum
    Sloanea_guianensis
    Sloanea_terniflora
    Solanum_dulcamara
    Sorbus_aria
    Sorocea_steinbachii
    Spondias_mombin
    Spondias_pinnata
    Sporobolus_interruptus
    Sterculia_speciosa
    Stipa_comata
    Tabernaemontana_arborea
    Tabernaemontana_donnell-smithii
    Tachigali_versicolor
    Talisia_princeps
    Tanacetum_corymbosum
    Tapirira_obtusa
    Taraxacum_campylodes
    Taxodium_distichum
    Taxus_baccata
    Terminalia_amazonia
    Theobroma_cacao
    Theobroma_speciosum
    Thevetia_ahouai
    Trattinnickia_aspera
    Triadica_cochinchinensis
    Trichilia_pleeana
    Trichilia_quadrijuga
    Trichospermum_mexicanum
    Trifolium_dubium
    Triplaris_cumingiana
    Ulmus_glabra
    Ulmus_minor
    Ulmus_pumila
    Ulmus_rubra
    Unonopsis_rufescens
    Vaccinium_myrtillus
    Vaccinium_vitis-idaea
    Verbena_bracteata
    Veronica_chamaedrys
    Viburnum_opulus
    Victoria_amazonica
    Viola_canina
    Vismia_baccifera
    Vitex_negundo
    Vitex_pinnata
    Ziziphus_mucronata
    Zuelania_guidoniaWarning: 
Parameter estimates appear at bounds:
    a
GEIGER-fitted comparative model of continuous data
 fitted ‘EB’ model parameters:
    a = -0.000001
    sigsq = 0.007246
    z0 = -1.725640

 model summary:
    log-likelihood = -147.274172
    AIC = 300.548344
    AICc = 300.658436
    free parameters = 3

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 39
    frequency of best fit = 0.390

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(OU.LDMC <- fitContinuous(phylo_traits$phy, LDMC[!is.na(LDMC)], model = "OU") )
Warning: The following tips were not found in 'data' and were dropped from 'phy':
    Abies_grandis
    Acacia_aroma
    Acacia_auriculiformis
    Acacia_doratoxylon
    Acacia_karroo
    Acacia_mearnsii
    Acacia_praecox
    Acer_monspessulanum
    Acer_saccharinum
    Acer_saccharum
    Albizia_saman
    Alliaria_petiolata
    Alnus_rhombifolia
    Ambrosia_psilostachya
    Anagallis_tenella
    Andira_inermis
    Anemone_nemorosa
    Annona_spraguei
    Apeiba_tibourbou
    Araucaria_araucana
    Arenaria_serpyllifolia
    Artemisia_californica
    Artemisia_dracunculus
    Aspidosperma_album
    Banksia_marginata
    Betula_papyrifera
    Betula_platyphylla
    Bombax_ceiba
    Briza_media
    Bromus_hordeaceus
    Bromus_sterilis
    Brosimum_utile
    Brownea_grandiceps
    Butea_monosperma
    Byrsonima_crassifolia
    Calamagrostis_epigejos
    Calophyllum_longifolium
    Campanula_rotundifolia
    Capsella_bursa-pastoris
    Carex_binervis
    Carex_hirta
    Carex_pallescens
    Carex_panicea
    Carya_glabra
    Caryocar_glabrum
    Casearia_sylvestris
    Castanea_dentata
    Ceanothus_spinosus
    Cecropia_obtusifolia
    Cecropia_peltata
    Ceiba_speciosa
    Cercocarpus_montanus_var._glaber
    Cinnamomum_subavenium
    Cirsium_wheeleri
    Cojoba_rufescens
    Colophospermum_mopane
    Cordia_bicolor
    Cordia_caffra
    Cordia_megalantha
    Cornus_glabrata
    Cornus_sericea
    Crepis_pyrenaica
    Croton_capitatus
    Cupania_rufescens
    Cupressus_sempervirens
    Cussonia_spicata
    Cytisus_scoparius
    Dacrydium_cupressinum
    Danthonia_decumbens
    Daphniphyllum_pentandrum
    Drimys_winteri
    Dryandra_sessilis
    Drypetes_standleyi
    Duguetia_quitarensis
    Ekebergia_capensis
    Elaeagnus_rhamnoides
    Emmotum_fagifolium
    Enterolobium_schomburgkii
    Epilobium_palustre
    Equisetum_arvense
    Equisetum_fluviatile
    Equisetum_palustre
    Erica_tetralix
    Erythrophleum_chlorostachys
    Eschweilera_decolorans
    Eschweilera_parviflora
    Eschweilera_sagotiana
    Eucalyptus_acmenoides
    Eucalyptus_grandis
    Eucalyptus_umbra
    Euphorbia_brachycera
    Euphorbia_corollata
    Faramea_occidentalis
    Fragaria_vesca
    Frangula_californica
    Fraxinus_uhdei
    Galium_verum
    Garcinia_macrophylla
    Grewia_monticola
    Griselinia_littoralis
    Guatteria_dumetorum
    Gymnosporia_heterophylla
    Hakea_cygna
    Hakea_preissii
    Hamamelis_virginiana
    Hampea_appendiculata
    Hampea_nutricia
    Heliocarpus_appendiculatus
    Heracleum_sphondylium
    Heteromeles_arbutifolia
    Holcus_mollis
    Holodiscus_discolor
    Ilex_mitis
    Inga_acreana
    Inga_goldmanii
    Inga_nobilis
    Inga_sapindoides
    Iris_missouriensis
    Iryanthera_juruensis
    Jacaranda_copaia
    Jacaratia_digitata
    Jacobaea_vulgaris
    Juncus_articulatus
    Juniperus_communis
    Kigelia_africana
    Koeleria_macrantha
    Lacmellea_panamensis
    Lactuca_muralis
    Laetia_corymbulosa
    Larix_kaempferi
    Larrea_cuneifolia
    Larrea_divaricata
    Lathyrus_pratensis
    Lecythis_zabucajo
    Leiospermum_racemosum
    Lepechinia_calycina
    Leptospermum_polygalifolium
    Leucadendron_salignum
    Ligustrum_lucidum
    Liquidambar_styraciflua
    Lotus_pedunculatus
    Lysimachia_vulgaris
    Lythrum_salicaria
    Macaranga_hypoleuca
    Machilus_chinensis
    Macrolobium_gracile
    Magnolia_grandiflora
    Malus_sylvestris
    Mangifera_indica
    Margaritaria_nobilis
    Melaleuca_leucadendra
    Metrosideros_polymorpha
    Mimulus_aurantiacus
    Molinia_caerulea
    Mosannona_garwoodii
    Murraya_paniculata
    Myrcia_splendens
    Nothofagus_betuloides
    Nothofagus_dombeyi
    Nothofagus_fusca
    Nothofagus_solandri
    Ochroma_pyramidale
    Ocotea_floribunda
    Ocotea_puberula
    Opuntia_sulphurea
    Ormosia_macrocalyx
    Orthion_oblanceolatum
    Ouratea_lucens
    Oxalis_acetosella
    Pachira_aquatica
    Pachira_insignis
    Packera_multilobata
    Palicourea_tetragona
    Parinari_campestris
    Parkinsonia_praecox
    Parnassia_palustris
    Peltogyne_venosa
    Perebea_xanthochyma
    Petrea_volubilis
    Picea_sitchensis
    Picramnia_latifolia
    Pimpinella_major
    Pinus_clausa
    Pinus_koraiensis
    Pinus_massoniana
    Piper_aequale
    Piscidia_carthagenensis
    Pistacia_lentiscus
    Pongamia_pinnata
    Populus_nigra
    Populus_tremuloides
    Pourouma_bicolor
    Pouteria_cladantha
    Pouteria_ephedrantha
    Pradosia_cochlearia
    Prosopis_flexuosa
    Prosopis_torquata
    Protium_tenuifolium
    Prunus_mahaleb
    Prunus_pensylvanica
    Pseudocymopterus_montanus
    Psidium_guajava
    Psychotria_marginata
    Pterocarpus_rohrii
    Pterygota_alata
    Quararibea_asterolepis
    Quassia_amara
    Quercus_lusitanica
    Quercus_robur
    Quercus_shumardii
    Quercus_stellata
    Quercus_variabilis
    Ranunculus_bulbosus
    Reynoutria_japonica
    Rhamnus_alaternus
    Rollinia_pittieri
    Rosa_woodsii
    Rubus_parviflorus
    Ruellia_humilis
    Rumex_obtusifolius
    Salix_cinerea
    Salix_lasiolepis
    Salix_nigra
    Schinopsis_marginata
    Schizolobium_parahyba
    Sequoia_sempervirens
    Shorea_leprosula
    Shorea_macroptera
    Shorea_parvifolia
    Shorea_smithiana
    Silphium_terebinthinaceum
    Sloanea_guianensis
    Sloanea_terniflora
    Solanum_dulcamara
    Sorbus_aria
    Sorocea_steinbachii
    Spondias_mombin
    Spondias_pinnata
    Sporobolus_interruptus
    Sterculia_speciosa
    Stipa_comata
    Tabernaemontana_arborea
    Tabernaemontana_donnell-smithii
    Tachigali_versicolor
    Talisia_princeps
    Tanacetum_corymbosum
    Tapirira_obtusa
    Taraxacum_campylodes
    Taxodium_distichum
    Taxus_baccata
    Terminalia_amazonia
    Theobroma_cacao
    Theobroma_speciosum
    Thevetia_ahouai
    Trattinnickia_aspera
    Triadica_cochinchinensis
    Trichilia_pleeana
    Trichilia_quadrijuga
    Trichospermum_mexicanum
    Trifolium_dubium
    Triplaris_cumingiana
    Ulmus_glabra
    Ulmus_minor
    Ulmus_pumila
    Ulmus_rubra
    Unonopsis_rufescens
    Vaccinium_myrtillus
    Vaccinium_vitis-idaea
    Verbena_bracteata
    Veronica_chamaedrys
    Viburnum_opulus
    Victoria_amazonica
    Viola_canina
    Vismia_baccifera
    Vitex_negundo
    Vitex_pinnata
    Ziziphus_mucronata
    Zuelania_guidonia
GEIGER-fitted comparative model of continuous data
 fitted ‘OU’ model parameters:
    alpha = 0.043364
    sigsq = 0.015990
    z0 = -1.648994

 model summary:
    log-likelihood = -94.773735
    AIC = 195.547471
    AICc = 195.657563
    free parameters = 3

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 34
    frequency of best fit = 0.340

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates
(null.LDMC <- fitContinuous(phylo_traits$phy, LDMC[!is.na(LDMC)], model = "white"))
Warning: The following tips were not found in 'data' and were dropped from 'phy':
    Abies_grandis
    Acacia_aroma
    Acacia_auriculiformis
    Acacia_doratoxylon
    Acacia_karroo
    Acacia_mearnsii
    Acacia_praecox
    Acer_monspessulanum
    Acer_saccharinum
    Acer_saccharum
    Albizia_saman
    Alliaria_petiolata
    Alnus_rhombifolia
    Ambrosia_psilostachya
    Anagallis_tenella
    Andira_inermis
    Anemone_nemorosa
    Annona_spraguei
    Apeiba_tibourbou
    Araucaria_araucana
    Arenaria_serpyllifolia
    Artemisia_californica
    Artemisia_dracunculus
    Aspidosperma_album
    Banksia_marginata
    Betula_papyrifera
    Betula_platyphylla
    Bombax_ceiba
    Briza_media
    Bromus_hordeaceus
    Bromus_sterilis
    Brosimum_utile
    Brownea_grandiceps
    Butea_monosperma
    Byrsonima_crassifolia
    Calamagrostis_epigejos
    Calophyllum_longifolium
    Campanula_rotundifolia
    Capsella_bursa-pastoris
    Carex_binervis
    Carex_hirta
    Carex_pallescens
    Carex_panicea
    Carya_glabra
    Caryocar_glabrum
    Casearia_sylvestris
    Castanea_dentata
    Ceanothus_spinosus
    Cecropia_obtusifolia
    Cecropia_peltata
    Ceiba_speciosa
    Cercocarpus_montanus_var._glaber
    Cinnamomum_subavenium
    Cirsium_wheeleri
    Cojoba_rufescens
    Colophospermum_mopane
    Cordia_bicolor
    Cordia_caffra
    Cordia_megalantha
    Cornus_glabrata
    Cornus_sericea
    Crepis_pyrenaica
    Croton_capitatus
    Cupania_rufescens
    Cupressus_sempervirens
    Cussonia_spicata
    Cytisus_scoparius
    Dacrydium_cupressinum
    Danthonia_decumbens
    Daphniphyllum_pentandrum
    Drimys_winteri
    Dryandra_sessilis
    Drypetes_standleyi
    Duguetia_quitarensis
    Ekebergia_capensis
    Elaeagnus_rhamnoides
    Emmotum_fagifolium
    Enterolobium_schomburgkii
    Epilobium_palustre
    Equisetum_arvense
    Equisetum_fluviatile
    Equisetum_palustre
    Erica_tetralix
    Erythrophleum_chlorostachys
    Eschweilera_decolorans
    Eschweilera_parviflora
    Eschweilera_sagotiana
    Eucalyptus_acmenoides
    Eucalyptus_grandis
    Eucalyptus_umbra
    Euphorbia_brachycera
    Euphorbia_corollata
    Faramea_occidentalis
    Fragaria_vesca
    Frangula_californica
    Fraxinus_uhdei
    Galium_verum
    Garcinia_macrophylla
    Grewia_monticola
    Griselinia_littoralis
    Guatteria_dumetorum
    Gymnosporia_heterophylla
    Hakea_cygna
    Hakea_preissii
    Hamamelis_virginiana
    Hampea_appendiculata
    Hampea_nutricia
    Heliocarpus_appendiculatus
    Heracleum_sphondylium
    Heteromeles_arbutifolia
    Holcus_mollis
    Holodiscus_discolor
    Ilex_mitis
    Inga_acreana
    Inga_goldmanii
    Inga_nobilis
    Inga_sapindoides
    Iris_missouriensis
    Iryanthera_juruensis
    Jacaranda_copaia
    Jacaratia_digitata
    Jacobaea_vulgaris
    Juncus_articulatus
    Juniperus_communis
    Kigelia_africana
    Koeleria_macrantha
    Lacmellea_panamensis
    Lactuca_muralis
    Laetia_corymbulosa
    Larix_kaempferi
    Larrea_cuneifolia
    Larrea_divaricata
    Lathyrus_pratensis
    Lecythis_zabucajo
    Leiospermum_racemosum
    Lepechinia_calycina
    Leptospermum_polygalifolium
    Leucadendron_salignum
    Ligustrum_lucidum
    Liquidambar_styraciflua
    Lotus_pedunculatus
    Lysimachia_vulgaris
    Lythrum_salicaria
    Macaranga_hypoleuca
    Machilus_chinensis
    Macrolobium_gracile
    Magnolia_grandiflora
    Malus_sylvestris
    Mangifera_indica
    Margaritaria_nobilis
    Melaleuca_leucadendra
    Metrosideros_polymorpha
    Mimulus_aurantiacus
    Molinia_caerulea
    Mosannona_garwoodii
    Murraya_paniculata
    Myrcia_splendens
    Nothofagus_betuloides
    Nothofagus_dombeyi
    Nothofagus_fusca
    Nothofagus_solandri
    Ochroma_pyramidale
    Ocotea_floribunda
    Ocotea_puberula
    Opuntia_sulphurea
    Ormosia_macrocalyx
    Orthion_oblanceolatum
    Ouratea_lucens
    Oxalis_acetosella
    Pachira_aquatica
    Pachira_insignis
    Packera_multilobata
    Palicourea_tetragona
    Parinari_campestris
    Parkinsonia_praecox
    Parnassia_palustris
    Peltogyne_venosa
    Perebea_xanthochyma
    Petrea_volubilis
    Picea_sitchensis
    Picramnia_latifolia
    Pimpinella_major
    Pinus_clausa
    Pinus_koraiensis
    Pinus_massoniana
    Piper_aequale
    Piscidia_carthagenensis
    Pistacia_lentiscus
    Pongamia_pinnata
    Populus_nigra
    Populus_tremuloides
    Pourouma_bicolor
    Pouteria_cladantha
    Pouteria_ephedrantha
    Pradosia_cochlearia
    Prosopis_flexuosa
    Prosopis_torquata
    Protium_tenuifolium
    Prunus_mahaleb
    Prunus_pensylvanica
    Pseudocymopterus_montanus
    Psidium_guajava
    Psychotria_marginata
    Pterocarpus_rohrii
    Pterygota_alata
    Quararibea_asterolepis
    Quassia_amara
    Quercus_lusitanica
    Quercus_robur
    Quercus_shumardii
    Quercus_stellata
    Quercus_variabilis
    Ranunculus_bulbosus
    Reynoutria_japonica
    Rhamnus_alaternus
    Rollinia_pittieri
    Rosa_woodsii
    Rubus_parviflorus
    Ruellia_humilis
    Rumex_obtusifolius
    Salix_cinerea
    Salix_lasiolepis
    Salix_nigra
    Schinopsis_marginata
    Schizolobium_parahyba
    Sequoia_sempervirens
    Shorea_leprosula
    Shorea_macroptera
    Shorea_parvifolia
    Shorea_smithiana
    Silphium_terebinthinaceum
    Sloanea_guianensis
    Sloanea_terniflora
    Solanum_dulcamara
    Sorbus_aria
    Sorocea_steinbachii
    Spondias_mombin
    Spondias_pinnata
    Sporobolus_interruptus
    Sterculia_speciosa
    Stipa_comata
    Tabernaemontana_arborea
    Tabernaemontana_donnell-smithii
    Tachigali_versicolor
    Talisia_princeps
    Tanacetum_corymbosum
    Tapirira_obtusa
    Taraxacum_campylodes
    Taxodium_distichum
    Taxus_baccata
    Terminalia_amazonia
    Theobroma_cacao
    Theobroma_speciosum
    Thevetia_ahouai
    Trattinnickia_aspera
    Triadica_cochinchinensis
    Trichilia_pleeana
    Trichilia_quadrijuga
    Trichospermum_mexicanum
    Trifolium_dubium
    Triplaris_cumingiana
    Ulmus_glabra
    Ulmus_minor
    Ulmus_pumila
    Ulmus_rubra
    Unonopsis_rufescens
    Vaccinium_myrtillus
    Vaccinium_vitis-idaea
    Verbena_bracteata
    Veronica_chamaedrys
    Viburnum_opulus
    Victoria_amazonica
    Viola_canina
    Vismia_baccifera
    Vitex_negundo
    Vitex_pinnata
    Ziziphus_mucronata
    Zuelania_guidonia
GEIGER-fitted comparative model of continuous data
 fitted ‘white’ model parameters:
    sigsq = 0.182623
    z0 = -1.591252

 model summary:
    log-likelihood = -126.267505
    AIC = 256.535011
    AICc = 256.589805
    free parameters = 2

Convergence diagnostics:
    optimization iterations = 100
    failed iterations = 0
    number of iterations with same best fit = 100
    frequency of best fit = 1.000

 object summary:
    'lik' -- likelihood function
    'bnd' -- bounds for likelihood search
    'res' -- optimization iteration summary
    'opt' -- maximum likelihood parameter estimates

Moderate signal

Check AIC weights

(aics_models <- setNames(c(AIC(BM.Height), AIC(EB.Height), AIC(OU.Height), AIC(null.Height)), c("BM", "EB", "OU", "NULL")) )
      BM       EB       OU     NULL 
1901.959 1903.966 1823.619 2160.758 
aic.w(aics_models)
  BM   EB   OU NULL 
   0    0    1    0 

Again, OU

So overall , the Ornstein-Uhlenbeck model seems to be the best model for most of our data, except Nmass which varies along a grand mean (white-noise).

3 Categorical traits

Check the categorical trait Growth Form

We will use the \(\delta\) statistic for this.

source("https://raw.githubusercontent.com/mrborges23/delta_statistic/master/code.R")

trait <- filtered_data %>% dplyr::select(Species, `Growth Form`) %>% column_to_rownames("Species") %>% .[rownames(phylo_traits$data),]

# This takes a very long time, go for lunch or something ...
#deltaA <- delta(trait,phylo_traits$phy,0.1,0.0589,10000,10,100)

And check the p-value by using randomization.

This can take a while again …

# random_delta <- rep(NA,100)
# for (i in 1:100){
#   rtrait <- sample(trait)
#   random_delta[i] <- delta(rtrait,phylo_traits$phy,0.1,0.0589,10000,10,100)
# }
# p_value <- sum(random_delta>deltaA)/length(random_delta)
# boxplot(random_delta)
# abline(h=deltaA,col="red")

For binary data you can uncomment the code bellow


4 Chosing imputation method

So now that we have an idea of the strength of the phylogenetic signal, it’s time to decide on a method of imputation.

Currently there are 3 main flavours of phylogenetic imputation see review in Molna-Venegas et al. 2018:

  • phylogenetic generalized linear models (pGLM; Swenson 2014, Goolsby et al. 2016a)
  • phylogenetic eigenvector regression models (PVR; DinizFilho et al. 1998)
  • phylogenetic eigenvector maps (PEM; GuĂ©nard et al. 2013)

pGLM can easily be implemented by fitting a pVCV matrix as the correlation structure in a gls model (package nlme). This assumes a Brownian model of evolution. More sophisticated ways of implementing a pGLM can be done with the package rPhylopars and several evolutionary models can be fitted. See tutorial here

PVR can be fit using a randomForest approach in the package missForest. One simply needs to transform the phylogeny into a phylogenetic distance matrix and use this in a PCoA. Then the PCoA axes are added as extra variables to the trait matrix as predictors.

PEM can be fit using the R package MPSEM. This expands on the PVR method by adding an evolutionary model and is thus considered more appropriate for evo-eco modelling. The tree is transformed prior to transforming into distance matrix and PCoA.

A fourth option would be to use Bayesian Hierarchical Matrix Factorization (BHPMF), which has been used by many publications for trait imputation. This method however doesn’t really use the phylogeny or quantify phylogenetic signal. It uses a taxonomic hierarchy and assumes that all ranks are equal and contain information. In reality, all taxonomic ranks are not equal, as some genera can be older than certain families for example. As with all Bayesian models, it will usually be much slower but will provide uncertainty of the prediction.

4.1 pGLM

Let’s see a simple example with pGLM

First, let’s randomly remove 10% of all values in the data frame. The missForest package can make this easy for us using the prodNA function.

set.seed(4312)
missing <- phylo_traits$data[,-1] %>% prodNA(0.1)

head(missing)

RPhylopars is a bit fussy and requires a species column (all lower case) that is the very first column in the trait matrix.

missing <- missing %>% 
  mutate(species = rownames(.)) %>% 
  dplyr::select(species, everything())

model.BM <- phylopars(tree = phylo_traits$phy, trait_data = missing, model = "BM")

warning: solve(): system is singular (rcond: 5.60546e-27); attempting approx solution

warning: solve(): system is singular (rcond: 1.80444e-33); attempting approx solution

warning: solve(): system is singular (rcond: 1.54393e-32); attempting approx solution

warning: solve(): system is singular (rcond: 3.53749e-29); attempting approx solution
model.BM
Phylogenetic trait variance-covariance
                   LA        Nmass          LMA          SSD       Height     Seedmass
LA        0.146855292 -0.002810604 -0.005329487 -0.004416941  0.044477787  0.052505321
Nmass    -0.002810604  0.060812588 -0.019895646 -0.018075167  0.033858310  0.029082235
LMA      -0.005329487 -0.019895646  0.021178855  0.008857747 -0.013069464 -0.001161069
SSD      -0.004416941 -0.018075167  0.008857747  0.012493858 -0.003575309 -0.005736158
Height    0.044477787  0.033858310 -0.013069464 -0.003575309  0.080096359  0.042377476
Seedmass  0.052505321  0.029082235 -0.001161069 -0.005736158  0.042377476  0.139364874

Brownian motion model
summary(model.BM)
Brownian motion model
         phylogenetic mean phylogenetic sd
LA               5.3937552          0.3832
Nmass            2.9001291          0.2466
LMA              4.5167510          0.1455
SSD             -1.2674510          0.1118
Height           0.7516388          0.2830
Seedmass        -2.6710471          0.3733

We can compare with other models. EB, OU, lambda and two variants of multivariate-OU (with full alpha or fixed alpha which corresponds to the adaptation rate parameter)

model.OU <- phylopars(trait_data = missing, tree = phylo_traits$phy, model = "OU")

model.mvOU <- phylopars(trait_data = missing, tree = phylo_traits$phy, model = "mvOU", full_alpha = TRUE, usezscores = FALSE) # the usezscores = F is a short term solution to a version issues on CRAN, see here https://github.com/ericgoolsby/Rphylopars/issues/54

model.mvOU_diag <- phylopars(trait_data = missing, tree = phylo_traits$phy, model = "mvOU", full_alpha = FALSE, usezscores = FALSE)

warning: solve(): system is singular (rcond: 2.35824e-132); attempting approx solution

warning: solve(): system is singular (rcond: 6.37787e-189); attempting approx solution

warning: solve(): system is singular (rcond: 2.25478e-23); attempting approx solution

warning: solve(): system is singular (rcond: 9.93784e-24); attempting approx solution

warning: solve(): system is singular (rcond: 1.88375e-206); attempting approx solution

warning: solve(): system is singular (rcond: 1.88375e-206); attempting approx solution

warning: solve(): system is singular (rcond: 5.16434e-234); attempting approx solution

warning: solve(): system is singular (rcond: 5.16434e-234); attempting approx solution

warning: solve(): system is singular (rcond: 6.6632e-32); attempting approx solution

warning: solve(): system is singular (rcond: 6.6632e-32); attempting approx solution

warning: solve(): system is singular (rcond: 1.83029e-18); attempting approx solution

warning: solve(): system is singular (rcond: 1.83029e-18); attempting approx solution

warning: solve(): system is singular (rcond: 2.59378e-57); attempting approx solution

warning: solve(): system is singular (rcond: 2.59378e-57); attempting approx solution

warning: solve(): system is singular (rcond: 2.59431e-57); attempting approx solution

warning: solve(): system is singular (rcond: 5.84998e-71); attempting approx solution

warning: solve(): system is singular (rcond: 5.84998e-71); attempting approx solution

warning: solve(): system is singular (rcond: 5.84998e-71); attempting approx solution

warning: solve(): system is singular (rcond: 4.97832e-18); attempting approx solution

warning: solve(): system is singular (rcond: 4.97832e-18); attempting approx solution

warning: solve(): system is singular (rcond: 4.97832e-18); attempting approx solution

warning: solve(): system is singular (rcond: 8.83805e-26); attempting approx solution

warning: solve(): system is singular (rcond: 8.83805e-26); attempting approx solution

warning: solve(): system is singular (rcond: 1.56371e-21); attempting approx solution

warning: solve(): system is singular (rcond: 1.56537e-21); attempting approx solution
model.EB <- phylopars(trait_data = missing, tree = phylo_traits$phy, model = "EB")

warning: solve(): system is singular (rcond: 4.3832e-17); attempting approx solution

warning: solve(): system is singular (rcond: nan); attempting approx solution

warning: solve(): system is singular (rcond: 1.38784e-39); attempting approx solution

warning: solve(): system is singular (rcond: nan); attempting approx solution

warning: solve(): system is singular (rcond: 2.30631e-27); attempting approx solution

warning: solve(): system is singular (rcond: nan); attempting approx solution

warning: solve(): system is singular (rcond: 5.0265e-108); attempting approx solution

warning: solve(): system is singular (rcond: nan); attempting approx solution

warning: solve(): system is singular (rcond: 3.43122e-54); attempting approx solution

warning: solve(): system is singular (rcond: 5.41758e-157); attempting approx solution

warning: solve(): system is singular (rcond: 2.66719e-17); attempting approx solution

warning: solve(): system is singular (rcond: 6.56869e-18); attempting approx solution

warning: solve(): system is singular (rcond: 2.42022e-37); attempting approx solution

warning: solve(): system is singular (rcond: 5.4724e-25); attempting approx solution
model.lambda <- phylopars(trait_data = missing, tree = phylo_traits$phy, model = "lambda")

warning: solve(): system is singular (rcond: 1.46268e-85); attempting approx solution

warning: solve(): system is singular (rcond: 3.32532e-215); attempting approx solution

warning: solve(): system is singular (rcond: 1.84798e-25); attempting approx solution

warning: solve(): system is singular (rcond: 1.80378e-65); attempting approx solution

warning: solve(): system is singular (rcond: 3.53515e-18); attempting approx solution

warning: solve(): system is singular (rcond: 7.05819e-87); attempting approx solution

warning: solve(): system is singular (rcond: 9.35436e-22); attempting approx solution

warning: solve(): system is singular (rcond: 4.40538e-72); attempting approx solution

warning: solve(): system is singular (rcond: 1.04763e-31); attempting approx solution

warning: solve(): system is singular (rcond: 6.86129e-42); attempting approx solution

warning: solve(): system is singular (rcond: 1.70055e-19); attempting approx solution

warning: solve(): system is singular (rcond: 1.53256e-18); attempting approx solution

warning: solve(): system is singular (rcond: 6.57095e-28); attempting approx solution

warning: solve(): system is singular (rcond: 6.59432e-27); attempting approx solution

warning: solve(): system is singular (rcond: 6.07831e-78); attempting approx solution

warning: solve(): system is singular (rcond: 6.01054e-17); attempting approx solution

warning: solve(): system is singular (rcond: 4.50702e-33); attempting approx solution

This can take quite a while to run.

We can compare the AICs of each model.

aics_models2 <- setNames(c(AIC(model.BM), AIC(model.OU), AIC(model.mvOU), AIC(model.mvOU_diag), AIC(model.EB), AIC(model.lambda)), c("BM", "OU", "mvOU", "mvOU_diag", "EB", "lambda"))

aics_models2
         BM          OU        mvOU   mvOU_diag          EB      lambda 
   8502.616 1029265.329   10272.851    6794.119    8505.241    6174.510 
aicw(aics_models2)
NA

Here a lambda transformation applied to the tree seems to fit the data best.

We can now get the imputed values from the model and use the variance to figure out their lower and upper 95% conf interval.

model.lambda$anc_recon[1:5,] %>% as_tibble() # means
model.lambda$anc_var[1:5,] %>% as_tibble() # variances
model.lambda$anc_recon[1:5,]- sqrt(model.lambda$anc_var[1:5,])*1.96 # Lower 95% CI
                                LA    Nmass      LMA       SSD     Height   Seedmass
Silphium_terebinthinaceum 9.988363 2.667228 5.430707 -1.073132 -2.1754462  2.8608171
Heliopsis_helianthoides   7.715864 3.432373 3.830609 -1.629847  0.2745473  1.5187908
Eupatorium_perfoliatum    9.598143 2.563614 4.274364 -1.483378 -1.6735469 -2.3848803
Gaillardia_pinnatifida    4.794798 2.638274 3.312451 -1.719268 -2.9397255  0.2126082
Hymenoxys_richardsonii    4.941642 2.888147 4.565949 -1.632553 -1.5606477 -4.0153066
model.lambda$anc_recon[1:5,]+ sqrt(model.lambda$anc_var[1:5,])*1.96 #Upper 95% CI
                                LA    Nmass      LMA       SSD     Height   Seedmass
Silphium_terebinthinaceum 9.988363 2.667228 5.430707 -1.073132 -2.1754462  2.8608171
Heliopsis_helianthoides   7.715864 3.432373 3.830609 -1.629847  0.2745473  1.5187908
Eupatorium_perfoliatum    9.598143 3.482349 4.274364 -1.483378  1.3419938 -2.3848803
Gaillardia_pinnatifida    4.794798 3.634277 4.746396 -1.719268 -2.9397255  0.2126082
Hymenoxys_richardsonii    4.941642 2.888147 4.565949 -1.632553 -1.5606477  1.3747380

You can see that the variance for Seedmass is quite high. For Solidago_rigida, the trait values go from -2.57 to 3.27.

# variance in Seedmass missing values imputed by Rphylopars
model.lambda$anc_var[which(is.na(missing$Seedmass)),"Seedmass"] %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  1.589   1.979   2.628   2.881   3.593   5.557 

We could use this to filter out imputed values that have too high a variance.

4.2 PVR

Now we will use the Phylogenetic Eigenvector Regression technique (PVR).

phylo.cor <- cophenetic.phylo(phylo_traits$phy)

# Check if the distance matrix is euclidean, if not, can square root the phylogeny
is.euclid(as.dist(phylo.cor))
[1] TRUE
phylo.pcoa <- dudi.pco(d = as.dist(phylo.cor), scannf = F, full = T)
# from ape, calculates broken stick automatically
phylo.pcoa2 <- pcoa(D = as.dist(phylo.cor))

Ok now we have our phylogenetic eigenevectors, the question is how many do we select to include into our regression?

Some people say all, some people say use a broken stick model, some people say use those that explain at least 50% of variance. See discussion by Rolhf , 2001 and Martins et al. 2002.

For now I will use the broken stick model. Some have suggested fa.parallel from package psych but I have had many issues with this. We can check this out visually and extract these from the object

ggplot() + geom_point(data=phylo.pcoa2$values, aes(x=1:nrow(phylo.pcoa2$values), y=Relative_eig), col="blue") + geom_point(data=phylo.pcoa2$values, aes(x=1:nrow(phylo.pcoa2$values), y=Broken_stick), col="red")


(sig.EV <-phylo.pcoa2$values[which(phylo.pcoa2$values$Relative_eig > phylo.pcoa2$values$Broken_stick),])
# Take the eigenvectors and add them to the traits matrix

missing_phylo <- cbind(missing, phylo.pcoa$li[,1:nrow(sig.EV)]) %>% dplyr::select(-species)

pvr <- missForest(xmis = missing_phylo, ntree = 1000, maxiter = 100, variablewise = T)

Check the output

head(pvr)
$ximp

$OOBerror
       MSE        MSE        MSE        MSE        MSE        MSE        MSE        MSE        MSE 
1.79489167 0.08689951 0.18307350 0.08737272 0.75754404 2.97288929 0.00000000 0.00000000 0.00000000 
       MSE        MSE        MSE        MSE        MSE        MSE        MSE        MSE        MSE 
0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 
       MSE        MSE        MSE        MSE        MSE        MSE        MSE        MSE        MSE 
0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 
       MSE        MSE        MSE        MSE        MSE        MSE        MSE        MSE        MSE 
0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 

We can see that the Out Of Bag Error (OOB) is much higher for trait 6 (ie. Seed Mass) with an OOB of 3.03 compared to others.

Compare the results of the true matrix, the rPhylopars approach and the missForest approach

phylo_traits$data[,-1]
pvr$ximp
data.frame(model.lambda$anc_recon[1:200,])

We can check how well this has done for each variable

# LMA
missing.LMA <- which(is.na(missing$LMA))

cor(phylo_traits$data[missing.LMA,"LMA"], pvr$ximp[missing.LMA,"LMA"]) %>% round(2) %>% paste("cor",.,"PVR")
[1] "cor 0.84 PVR"
cor(phylo_traits$data[missing.LMA,"LMA"], model.lambda$anc_recon[missing.LMA,"LMA"]) %>% round(2) %>% paste("cor",.,"pGLM")
[1] "cor 0.82 pGLM"
#LA
missing.LA <- which(is.na(missing$LA))

cor(phylo_traits$data[missing.LA,"LA"], pvr$ximp[missing.LA,"LA"]) %>% round(2) %>% paste("cor",.,"PVR")
[1] "cor 0.7 PVR"
cor(phylo_traits$data[missing.LA,"LA"], model.lambda$anc_recon[missing.LA,"LA"]) %>% round(2) %>% paste("cor",.,"pGLM")
[1] "cor 0.82 pGLM"

Not great here for both of them. pGLM is much better though.

#Nmass
missing.Nmass <- which(is.na(missing$Nmass))

cor(phylo_traits$data[missing.Nmass,"Nmass"], pvr$ximp[missing.Nmass,"Nmass"]) %>% round(2) %>% paste("cor",.,"PVR")
[1] "cor 0.6 PVR"
cor(phylo_traits$data[missing.Nmass,"Nmass"], model.lambda$anc_recon[missing.Nmass,"Nmass"]) %>% round(2) %>% paste("cor",.,"pGLM")
[1] "cor 0.64 pGLM"
#SSD
missing.SSD <- which(is.na(missing$SSD))

cor(phylo_traits$data[missing.SSD,"SSD"], pvr$ximp[missing.SSD,"SSD"]) %>% round(2) %>% paste("cor",.,"PVR")
[1] "cor 0.87 PVR"
cor(phylo_traits$data[missing.SSD,"SSD"], model.lambda$anc_recon[missing.SSD,"SSD"]) %>% round(2) %>% paste("cor",.,"pGLM")
[1] "cor 0.86 pGLM"
#Seedmass
missing.Seedmass <- which(is.na(missing$Seedmass))

cor(phylo_traits$data[missing.Seedmass,"Seedmass"], pvr$ximp[missing.Seedmass,"Seedmass"]) %>% round(2) %>% paste("cor",.,"PVR")
[1] "cor 0.81 PVR"
cor(phylo_traits$data[missing.Seedmass,"Seedmass"], model.lambda$anc_recon[missing.Seedmass,"Seedmass"]) %>% round(2) %>% paste("cor",.,"pGLM")
[1] "cor 0.84 pGLM"

And height

#Height
missing.Height <- which(is.na(missing$Height))

cor(phylo_traits$data[missing.Height,"Height"], pvr$ximp[missing.Height,"Height"]) %>% round(2) %>% paste("cor",.,"PVR")
[1] "cor 0.88 PVR"
cor(phylo_traits$data[missing.Height,"Height"], model.lambda$anc_recon[missing.Height,"Height"]) %>% round(2) %>% paste("cor",.,"pGLM")
[1] "cor 0.86 pGLM"

Ok so overall it’s not too bad! Both methods are very comparable and only Leaf Area and Nitrogen Mass are poorly reconstructed.

What happens if we try and impute these without phylogenetic information?

impute.nophylo <- missForest(xmis = missing[,-1], ntree = 1000, maxiter = 100, variablewise = T)
cor(phylo_traits$data[missing.LA,"LA"], impute.nophylo$ximp[missing.LA,"LA"])
[1] 0.4948403
cor(phylo_traits$data[missing.Nmass,"Nmass"], impute.nophylo$ximp[missing.Nmass,"Nmass"])
[1] 0.6032527

Ok so for Leaf Area, it’s a tad worse than when using phylogeny, for Nmass it’s the same. This is because Leaf Area doesn’t have super strong phylogenetic signal and Nmass has no phylogenetic signal at all and is best fitted by a white-noise model.

It is also possible that the first phylogenetic eigenvectors that we selected aren’t the ones that best suit Leaf Area, so the PVR method does poorly.

We can check the out of bag error

rbind(pvr$OOBerror[1:6], impute.nophylo$OOBerror) %>% `colnames<-`(names(impute.nophylo$ximp)) %>% `rownames<-`(c("RF_PVR", "RF_no_phylo"))
                  LA      Nmass       LMA        SSD    Height Seedmass
RF_PVR      1.794892 0.08689951 0.1830735 0.08737272 0.7575440 2.972889
RF_no_phylo 2.106433 0.10523274 0.1649866 0.10091698 0.9563365 5.098242

Interesting, the OOB error for NMass is quite low compared to others, but the correlation of the imputed values is bad.

Let’s quickly check the correlation between traits

cor(phylo_traits$data[,-1])
                  LA       Nmass         LMA         SSD     Height    Seedmass
LA        1.00000000  0.09317154 -0.06162798  0.08995956  0.4248158  0.40655217
Nmass     0.09317154  1.00000000 -0.56983896 -0.33316314 -0.1838869 -0.06222018
LMA      -0.06162798 -0.56983896  1.00000000  0.56796971  0.3988510  0.31703906
SSD       0.08995956 -0.33316314  0.56796971  1.00000000  0.7194188  0.57530529
Height    0.42481579 -0.18388692  0.39885096  0.71941879  1.0000000  0.67603235
Seedmass  0.40655217 -0.06222018  0.31703906  0.57530529  0.6760323  1.00000000

4.3 PEM

This part is very much experimental, and I don’t quite have a strong grip on it yet.

library(MPSEM)
Warning: package ‘MPSEM’ was built under R version 4.2.3
tree.pgraph <- Phylo2DirectedGraph(phylo_traits$phy)
tree.PEM <-PEM.build(tree.pgraph, d="distance",sp="species") # here I'm not adding any a or psi parameters
as.data.frame(tree.PEM)

This next solution requires complete trait values, no NAs.

# here we can let the data speak for itself
# use RMLE to estimate a 
tree.PEM_opt2 <- PEM.fitSimple(
y = as.matrix(phylo_traits$data[,-1]),
x=NULL,
w = tree.pgraph,
d = "distance", sp="species",
lower = 0, upper = 1)

The package uses a linear model via stepwise selection based on AIC criterion. It can only do it for one trait at a time and can accept a certain amount of NA values.

## Estimating ancestral trait values:
ANCloc <- getAncGraphLocations(tree.pgraph)
PEMfsAnc <- PEM.fitSimple(y=as.matrix(phylo_traits$data[,-1]),
                          x=NULL,
                          w=ANCloc$x,
                          d="distance",sp="species",
                          lower=0,upper=1)
PEManc1 <- Locations2PEMscores(PEMfsAnc, ANCloc)
# there is another step to this section that's i can't figure how to work out yet

Now use these in the imputation method with RF.

missing_phylo <- cbind(missing, as.data.frame(tree.PEM_opt2)[,1:ncol(sig.EV)]) %>% dplyr::select(-species)

pem <- missForest(xmis = missing_phylo, ntree = 1000, maxiter = 100, variablewise = T)

Check

# LMA
missing.LMA <- which(is.na(missing$LMA))

cor(phylo_traits$data[missing.LMA,"LMA"], pvr$ximp[missing.LMA,"LMA"]) %>% round(2) %>% paste("cor",.,"PVR")
[1] "cor 0.84 PVR"
cor(phylo_traits$data[missing.LMA,"LMA"], model.lambda$anc_recon[missing.LMA,"LMA"]) %>% round(2) %>% paste("cor",.,"pGLM")
[1] "cor 0.82 pGLM"
cor(phylo_traits$data[missing.LMA,"LMA"], pem$ximp[missing.LMA,"LMA"]) %>% round(2) %>% paste("cor",.,"PEM")
[1] "cor 0.81 PEM"
#LA
missing.LA <- which(is.na(missing$LA))

cor(phylo_traits$data[missing.LA,"LA"], pvr$ximp[missing.LA,"LA"]) %>% round(2) %>% paste("cor",.,"PVR")
[1] "cor 0.7 PVR"
cor(phylo_traits$data[missing.LA,"LA"], model.lambda$anc_recon[missing.LA,"LA"]) %>% round(2) %>% paste("cor",.,"pGLM")
[1] "cor 0.82 pGLM"
cor(phylo_traits$data[missing.LA,"LA"], pem$ximp[missing.LA,"LA"]) %>% round(2) %>% paste("cor",.,"PEM")
[1] "cor 0.66 PEM"
#Nmass
missing.Nmass <- which(is.na(missing$Nmass))

cor(phylo_traits$data[missing.Nmass,"Nmass"], pvr$ximp[missing.Nmass,"Nmass"]) %>% round(2) %>% paste("cor",.,"PVR")
[1] "cor 0.6 PVR"
cor(phylo_traits$data[missing.Nmass,"Nmass"], model.lambda$anc_recon[missing.Nmass,"Nmass"]) %>% round(2) %>% paste("cor",.,"pGLM")
[1] "cor 0.64 pGLM"
cor(phylo_traits$data[missing.Nmass,"Nmass"], pem$ximp[missing.Nmass,"Nmass"]) %>% round(2) %>% paste("cor",.,"PEM")
[1] "cor 0.6 PEM"

So very similar to PVR if not worse, not much of an improvement really … Unless I’m doing this wrong?


5 Combining the best of both worlds

One solution is to run several rounds of imputation.

One could use Rphylopars to first impute the continuous variables that have strong phylogenetic signal and then run phylogenetic eigenvectors with RF to impute the remaining categorical traits and traits that have low phylogenetic signal.

Let’s re-run the lambda model but remove NMass. Then we will add Nmass and the phylogenetic eigenvectors.

model.lambda$anc_recon %>% dim()
[1] 999   6
model.lambda2 <- phylopars(trait_data = missing %>% dplyr::select(-Nmass) , tree = phylo_traits$phy, model = "lambda")

warning: solve(): system is singular (rcond: 1.84019e-22); attempting approx solution

warning: solve(): system is singular (rcond: 3.38169e-222); attempting approx solution

warning: solve(): system is singular (rcond: 3.0712e-17); attempting approx solution

warning: solve(): system is singular (rcond: 1.82178e-17); attempting approx solution

warning: solve(): system is singular (rcond: 6.09341e-41); attempting approx solution

warning: solve(): system is singular (rcond: 2.34904e-75); attempting approx solution

warning: solve(): system is singular (rcond: 1.20236e-19); attempting approx solution

warning: solve(): system is singular (rcond: 2.8273e-26); attempting approx solution

warning: solve(): system is singular (rcond: 2.27438e-31); attempting approx solution

Now add the Nmass

comb.model <- data.frame(model.lambda2$anc_recon[1:500,], Nmass=missing$Nmass, phylo.pcoa$li[,1:nrow(sig.EV)])

pvr.comb <- missForest(xmis = comb.model, ntree = 1000, maxiter = 100, variablewise = T)

And now extract these and compare

# LMA
missing.LMA <- which(is.na(missing$LMA))

cor(phylo_traits$data[missing.LMA,"LMA"], pvr$ximp[missing.LMA,"LMA"]) %>% round(2) %>% paste("cor",.,"PVR")
[1] "cor 0.84 PVR"
cor(phylo_traits$data[missing.LMA,"LMA"], model.lambda$anc_recon[missing.LMA,"LMA"]) %>% round(2) %>% paste("cor",.,"pGLM")
[1] "cor 0.82 pGLM"
cor(phylo_traits$data[missing.LMA,"LMA"], pem$ximp[missing.LMA,"LMA"]) %>% round(2) %>% paste("cor",.,"PEM")
[1] "cor 0.81 PEM"
cor(phylo_traits$data[missing.LMA,"LMA"], pvr.comb$ximp[missing.LMA,"LMA"]) %>% round(2) %>% paste("cor",.,"combined")
[1] "cor 0.75 combined"

Nmass

#Nmass
missing.Nmass <- which(is.na(missing$Nmass))

cor(phylo_traits$data[missing.Nmass,"Nmass"], pvr$ximp[missing.Nmass,"Nmass"]) %>% round(2) %>% paste("cor",.,"PVR")
[1] "cor 0.6 PVR"
cor(phylo_traits$data[missing.Nmass,"Nmass"], model.lambda$anc_recon[missing.Nmass,"Nmass"]) %>% round(2) %>% paste("cor",.,"pGLM")
[1] "cor 0.64 pGLM"
cor(phylo_traits$data[missing.Nmass,"Nmass"], pem$ximp[missing.Nmass,"Nmass"]) %>% round(2) %>% paste("cor",.,"PEM")
[1] "cor 0.6 PEM"
cor(phylo_traits$data[missing.Nmass,"Nmass"], pvr.comb$ximp[missing.Nmass,"Nmass"]) %>% round(2) %>% paste("cor",.,"combined")
[1] "cor 0.58 combined"

Interesting, it seems like the combined method is actually worse than any method!

What if we include Nmass in the original imputation, but then remove those results and re-run the randomForest ?

model.lambda3 <- phylopars(trait_data = missing , tree = phylo_traits$phy, model = "lambda")

warning: solve(): system is singular (rcond: 1.46268e-85); attempting approx solution

warning: solve(): system is singular (rcond: 3.32532e-215); attempting approx solution

warning: solve(): system is singular (rcond: 1.84798e-25); attempting approx solution

warning: solve(): system is singular (rcond: 1.80378e-65); attempting approx solution

warning: solve(): system is singular (rcond: 3.53515e-18); attempting approx solution

warning: solve(): system is singular (rcond: 7.05819e-87); attempting approx solution

warning: solve(): system is singular (rcond: 9.35436e-22); attempting approx solution

warning: solve(): system is singular (rcond: 4.40538e-72); attempting approx solution

warning: solve(): system is singular (rcond: 1.04763e-31); attempting approx solution

warning: solve(): system is singular (rcond: 6.86129e-42); attempting approx solution

warning: solve(): system is singular (rcond: 1.70055e-19); attempting approx solution

warning: solve(): system is singular (rcond: 1.53256e-18); attempting approx solution

warning: solve(): system is singular (rcond: 6.57095e-28); attempting approx solution

warning: solve(): system is singular (rcond: 6.59432e-27); attempting approx solution

warning: solve(): system is singular (rcond: 6.07831e-78); attempting approx solution

warning: solve(): system is singular (rcond: 6.01054e-17); attempting approx solution

warning: solve(): system is singular (rcond: 4.50702e-33); attempting approx solution
comb.model2 <- data.frame(model.lambda3$anc_recon[1:500,-2], Nmass=missing$Nmass, phylo.pcoa$li[,1:nrow(sig.EV)])
pvr.comb2 <- missForest(xmis = comb.model2, ntree = 1000, maxiter = 100, variablewise = T)

#LMA
cor(phylo_traits$data[missing.LMA,"LMA"], pvr$ximp[missing.LMA,"LMA"]) %>% round(2) %>% paste("cor",.,"PVR")
[1] "cor 0.84 PVR"
cor(phylo_traits$data[missing.LMA,"LMA"], model.lambda$anc_recon[missing.LMA,"LMA"]) %>% round(2) %>% paste("cor",.,"pGLM")
[1] "cor 0.82 pGLM"
cor(phylo_traits$data[missing.LMA,"LMA"], pem$ximp[missing.LMA,"LMA"]) %>% round(2) %>% paste("cor",.,"PEM")
[1] "cor 0.81 PEM"
cor(phylo_traits$data[missing.LMA,"LMA"], pvr.comb2$ximp[missing.LMA,"LMA"]) %>% round(2) %>% paste("cor",.,"combined")
[1] "cor 0.82 combined"
#Nmass

cor(phylo_traits$data[missing.Nmass,"Nmass"], pvr$ximp[missing.Nmass,"Nmass"]) %>% round(2) %>% paste("cor",.,"PVR")
[1] "cor 0.6 PVR"
cor(phylo_traits$data[missing.Nmass,"Nmass"], model.lambda$anc_recon[missing.Nmass,"Nmass"]) %>% round(2) %>% paste("cor",.,"pGLM")
[1] "cor 0.64 pGLM"
cor(phylo_traits$data[missing.Nmass,"Nmass"], pem$ximp[missing.Nmass,"Nmass"]) %>% round(2) %>% paste("cor",.,"PEM")
[1] "cor 0.6 PEM"
cor(phylo_traits$data[missing.Nmass,"Nmass"], pvr.comb2$ximp[missing.Nmass,"Nmass"]) %>% round(2) %>% paste("cor",.,"combined")
[1] "cor 0.58 combined"

Ok, so even here it’s not as good. interesting.

Perhaps this is best done only with remaining categorical traits.

6 BHPMF

Final test

BHPMF.test <- GapFilling(trait.info, hierarchy.info,
                         mean.gap.filled.output.path = paste0(tmp.dir, "/mean_gap_filled.txt"),
                         std.gap.filled.output.path = paste0(tmp.dir, "/std_gap_filled.txt"),
                         tmp.dir = tmp.dir)
./fold1/Tunning 
RMSE for the test data:  0.7519486

LS0tDQp0aXRsZTogIlBoeWxvZ2VuZXRpYyBpbXB1dGF0aW9uIg0KYXV0aG9yOiAiTWF0aGV3IFJlZXMiDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIGNvZGVfZm9sZGluZzogImhpZGUiDQogICAgZGZfcHJpbnQ6ICJwYWdlZCINCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDMNCiAgICB0b2NfZmxvYXQ6IHRydWUNCi0tLQ0KDQojIFByZW1pc2NlDQoNCkluIHRoaXMgTm90ZWJvb2sgSSB3YW50IHRvIGV4cGxvcmUgbWV0aG9kcyBvZiBwaHlsb2dlbmV0aWMgaW1wdXRhdGlvbiwgdGhhdCBpcywgdXNpbmcgcGh5bG9nZW5ldGljIGluZm9ybWF0aW9uIHRvIGltcHV0ZSBtaXNzaW5nIHZhbHVlcyBpbiBhbnkgZ2l2ZW4gZGF0YXNldCwgYmUgdGhlc2UgY29udGludW91cyAoaW5jbHVkaW5nIGNvdW50KSBvciBkaXNjcmV0ZS4NCg0KSXQgaXMgaW1wb3J0YW50IHRvIHVuZGVyc3RhbmQgdGhhdCBtb3N0IG1ldGhvZHMgaW1wbGljaXRseSByZWx5IG9uIHRoZSBhc3N1bXB0aW9uIHRoYXQgdGhlIHBoeWxvZ2VueSB3aWxsIGhlbHAgcHJlZGljdCB0aGUgdHJhaXRzIGJldHRlci4gRWl0aGVyIGJlY2F1c2UgY2xvc2VseSByZWxhdGVkIHNwZWNpZXMgdGVuZCB0byBzaGFyZSBtb3JlIHNpbWlsYXIgdHJhaXQgdmFsdWVzIHRoYW4gZGlzdGFudGx5IHJlbGF0ZWQgc3BlY2llcywgb3IgYmVjYXVzZSB3ZSBhc3N1bWUgdGhhdCB0cmFpdHMgYXJlIGNvbnN0cmFpbmVkIGJ5IGV2b2x1dGlvbiBhY2NvcmRpbmcgdG8gYSBjZXJ0YWluIG1vZGVsIChsaWtlIEJyb3duaWFuIE1vdGlvbikuIFRoaXMgaXMgb2YgY291cnNlIG5vdCBhbHdheXMgdHJ1ZS4gVGh1cyBpcyBpdCBhbHNvIGltcG9ydGFudCB0byB0ZXN0IGZvciBwaHlsb2dlbmV0aWMgc2lnbmFsIGluIGFueSBnaXZlbiB0cmFpdCAobW9yZSBvbiB0aGlzIGxhdGVyKS4gSWYgdHJhaXRzIGRvIG5vdCBzaG93IGFueSBwaHlsb2dlbmV0aWMgc2lnbmFsLCB0aGVuIHRyeWluZyB0byBpbXB1dGUgdGhlbSB1c2luZyB0aGUgcGh5bG9nZW55IGJlY29tZXMgdW5pbmZvcm1hdGl2ZSBhbmQgY2FuIHBvdGVudGlhbGx5IHJlZHVjZSB0aGUgcHJlY2lzaW9uIG9mIHRoZSBpbXB1dGVkIHZhbHVlcy4NCg0KT25lIHBlcnNvbmFsIG9ic2VydmF0aW9uIGlzIHRoYXQgcGh5bG9nZW5ldGljIHNpZ25hbCB3aWxsIGluY3JlYXNlIHdpdGggdGhlIHNpemUgb2YgdGhlIGRhdGFzZXQuIFRoYXQgaXMsIGlmIHlvdSBhcmUgdHJ5aW5nIHRvIHByZWRpY3QgdHJhaXRzIGZvciBhIGxpc3Qgb2YgMTAwIHNwZWNpZXMsIHlvdSBtaWdodCBmaW5kIHRoYXQgdXNpbmcgYSBwaHlsb2dlbnkgd2l0aCBvbmx5IDEwMCB0aXBzIHdpbGwgcGVyZm9ybSBwb29ybHkgcmVsYXRpdmUgdG8gYSBwaHlsb2dlbnkgb2YgMSwwMDAgdGlwcy4NCg0KSXQgaXMgYWxzbyBwb3NzaWJsZSB0byBpbmNvcnBvcmF0ZSBhIGNvcnJlbGF0aW9uIHN0cnVjdHVyZSBsaW5rZWQgdG8gb3RoZXIgdHJhaXRzIHRoYXQgbWlnaHQgYmUgaGVscGZ1bCBpbiBpbXB1dGluZyB0aGUgbWlzc2luZyB2YWx1ZXMsIGp1c3QgbGlrZSB5b3Ugd291bGQgZG8gaWYgeW91IHVzZWQgYSBsaW5lYXIgbW9kZWwsIG9yIGltcHV0YXRpb24gYnkgUENBIG9yIGV2ZW4gTXVsdGlwbGUgSW1wdXRhdGlvbiBieSBDaGFpbmVkIEVxdWF0aW9ucyAoTUlDRSkuDQoNCkZpbmFsbHksIGl0IHdpbGwgYmUgaW1wb3J0YW50IHRvIGhhdmUgYSBtZWFzdXJlIG9mIHZhbGlkYXRpb24gb2YgdGhlIGltcHV0ZWQgdmFsdWVzLiBBIGNyb3NzIHZhbGlkYXRpb24gb3IgbW9udGUgY2FybG8gcHJvY2VkdXJlIGNhbiBiZSB1c2VkIGZvciB0aGlzIChzZWUgTW9saW5hLVZlbmVnYXMsIDIwMjMpLg0KDQojIE1lYXN1cmluZyBwaHlsb2dlbmV0aWMgc2lnbmFsDQoNCkxvYWQgdGhlIGxpYnJhcmllcw0KDQpgYGB7ciwgd2FybmluZz1GLCBlcnJvcj1GLCBtZXNzYWdlPUZ9DQpsaWJyYXJ5KHBoeXRvb2xzKQ0KbGlicmFyeShwaGFuZ29ybikNCmxpYnJhcnkoUnBoeWxvcGFycykNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShhcGUpDQpsaWJyYXJ5KGdlaWdlcikNCmxpYnJhcnkobWlzc0ZvcmVzdCkNCmxpYnJhcnkoZnVuc3BhY2UpDQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkoY2FyKQ0KbGlicmFyeShwaWNhbnRlKQ0KbGlicmFyeShIbWlzYykNCmxpYnJhcnkoYnJvb20pDQpsaWJyYXJ5KFBlcmZvcm1hbmNlQW5hbHl0aWNzKQ0KbGlicmFyeShhZGU0KQ0KYGBgDQoNCg0KT2ssIGxldCdzIGdldCBzdGFydGVkIHdpdGggc29tZSBzaW1wbGUgZXhhbXBsZXMuDQoNCkxldHMgYXNzdW1lIHRoYXQgb3VyIGRhdGFzZXQgY29uc2lzdHMgb2YgY29udGludW91cyBhbmQgZGlzY3JldGUgZGF0YS4NCkhlcmUgd2UgY2FuIHN0YXJ0IGJ5IHVzaW5nIHRoZSBkYXRhc2V0IGZyb20gW1NhbmRyYSBkaWF6IGV0IGFsLiwgMjAyMl0oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9zNDE1OTctMDIyLTAxNzc0LTkpIHdoaWNoIGlzIGFuIGVuaGFuY2VkIGRhdGFzZXQgY29udGFpbmluZyB0aGUgNiBtYWpvciB0cmFpdHMgb2YgdGhlIGdsb2JhbCBzcGVjdHJ1bSBvZiBwbGFudCBmb3JtIGFuZCBmdW5jdGlvbiAoRGlheiBldCBhbC4gMjAxNikgcGx1cyBzb21lIG90aGVyIGluZm9ybWF0aW9uIGZvciBhYm91dCA0NiwwMDAgc3BlY2llcy4NCg0KDQpgYGB7ciwgZXJyb3I9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkYXRhIDwtIHJlYWRfeGxzeCgiLi4vRnVuY3Rpb25hbCB0cmFpdHMvRGlheiBldCBhbC4gMjAyMi4gRW5oYW5jZWQgVHJhaXQgZGF0YWJhc2UvVHJ5MjAyMzEyMzE4NDMzMTQ4MF9EYXRhc2V0L0RhdGFzZXQvU3BlY2llc19tZWFuX3RyYWl0cy54bHN4Iiwgc2hlZXQgPSAxLCBjb2xfbmFtZXMgPSBUKSAlPiUgDQogIGRwbHlyOjpyZW5hbWUoU3BlY2llcyA9IGBTcGVjaWVzIG5hbWUgc3RhbmRhcmRpemVkIGFnYWluc3QgVFBMYCwgDQogICAgICAgICAgICAgICAgTERNQyA9IGBMRE1DIChnL2cpYCwgDQogICAgICAgICAgICAgICAgTEE9YExlYWYgYXJlYSAobW0yKWAsIA0KICAgICAgICAgICAgICAgIE5tYXNzPWBObWFzcyAobWcvZylgLCANCiAgICAgICAgICAgICAgICBMTUEgPSBgTE1BIChnL20yKWAsIA0KICAgICAgICAgICAgICAgIEhlaWdodCA9IGBQbGFudCBoZWlnaHQgKG0pYCwNCiAgICAgICAgICAgICAgICBTZWVkbWFzcyA9IGBEaWFzcG9yZSBtYXNzIChtZylgLA0KICAgICAgICAgICAgICAgIFNTRCA9IGBTU0QgY29tYmluZWQgKG1nL21tMylgKQ0KDQpkYXRhJFNwZWNpZXMgPC0gZ3N1YigiICIsICJfIiwgZGF0YSRTcGVjaWVzKQ0KDQpkYXRhIDwtIHN0cmluZ3MyZmFjdG9ycyhkYXRhKQ0KDQpzdHIoZGF0YSkNCmBgYA0KDQpXZSBjYW4gc3RhcnQgYnkgZmlsdGVyaW5nIHRoaXMgZGF0c2V0IHRvIHNlbGVjdCBvbmx5IHRoZSBzcGVjaWVzIHdoaWNoIGhhdmUgYWxsIG9ic2VydmF0aW9ucyBvZiB0cmFpdHMgYW5kIGFyZSBzdWJnZW5lcmljIGxldmVsIG9ic2VydmF0aW9ucy4NCg0KYGBge3J9DQpmaWx0ZXJlZF9kYXRhIDwtIGRhdGEgJT4lIGZpbHRlcihgTnVtYmVyIG9mIHRyYWl0cyB3aXRoIHZhbHVlc2AgPiA1ICYgYFRheG9ub21pYyBsZXZlbGAgIT0gImdlbnVzIikNCg0KZGltKGZpbHRlcmVkX2RhdGEpDQpgYGANCg0KT2sgc28gdGhhdCdzIDIyMTQgc3BlY2llcyBsZWZ0LiANCg0KQ2hlY2sgaWYgdGhlcmUgYXJlIGFueSBOQXMNCg0KYGBge3J9DQpjYXQoc3VtKGlzLm5hKGZpbHRlcmVkX2RhdGEpKSAvIChucm93KGZpbHRlcmVkX2RhdGEpKm5jb2woZmlsdGVyZWRfZGF0YSkpICogMTAwLCAiJSIpDQpgYGANClNvIHN0aWxsIGFib3V0IDE1JSBtaXNzaW5nIGRhdGEsIHRoYXQgaXMgZnJvbSB0aGUgY29sdW1uIExETUMuDQoNCkNoZWNrIHRoZSBkaXN0cmlidXRpb24gb2YgZWFjaCB2YXJpYWJsZS4NCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIGVycm9yPUZBTFNFfQ0KZmlsdGVyZWRfZGF0YSAlPiUgZHBseXI6OnNlbGVjdChMRE1DLCBMQSwgTm1hc3MsIExNQSwgU1NELCBIZWlnaHQsIFNlZWRtYXNzKSAlPiUgDQogIGdhdGhlciAlPiUgDQogIGdncGxvdChhZXModmFsdWUsIGZpbGw9a2V5KSkgKyBnZW9tX2RlbnNpdHkoKSArIGZhY2V0X3dyYXAoZmFjZXRzID0gfmtleSwgc2NhbGVzID0gImZyZWUiKQ0KYGBgDQoNCk5vdyBsb2cgdGhlbSBhbGwgYW5kIGNoZWNrIHRoZXkgYXJlIG1vcmUgb3IgbGVzcyBub3JtYWxseSBkaXN0cmlidXRlZC4NCg0KYGBge3J9DQoNCm5ldyA8LSBmaWx0ZXJlZF9kYXRhICU+JSBjb2x1bW5fdG9fcm93bmFtZXMoIlNwZWNpZXMiKSAlPiUgDQogIGRwbHlyOjpzZWxlY3QoTERNQywgTEEsIE5tYXNzLCBMTUEsIFNTRCwgSGVpZ2h0LCBTZWVkbWFzcykgJT4lIGxvZygpIA0KDQpuZXcgJT4lICANCiAgZ2F0aGVyICU+JSANCiAgZ2dwbG90KGFlcyh2YWx1ZSwgZmlsbD1rZXkpKSArIGdlb21fZGVuc2l0eSgpICsgZmFjZXRfd3JhcChmYWNldHMgPSB+a2V5LCBzY2FsZXMgPSAiZnJlZSIpDQoNCiMgQ2hlY2sgZm9yIG5vcm1hbGl0eQ0KYXBwbHkobmV3LCAyLCBzaGFwaXJvLnRlc3QpICU+JSBkby5jYWxsKHJiaW5kLCAuKQ0KYGBgDQoNCk9rIHNvIGl0J3Mgbm90IHBlcmZlY3QgYnV0IGl0IGlzIG11Y2ggYmV0dGVyLg0KDQpMZXQncyBzZWUgd2hhdCB0aGUgY29ycmVsYXRpb25zIGJldHdlZW4gdHJhaXRzIGFyZS4NCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIGVycm9yPUZBTFNFfQ0KDQpuZXcgJT4lIA0KICBhcy5tYXRyaXgoKSAlPiUgDQogIEhtaXNjOjpyY29ycigpICU+JSANCiAgYnJvb206OnRpZHkoKQ0KDQpuZXcgJT4lIA0KICBjaGFydC5Db3JyZWxhdGlvbigpDQpgYGANCg0KU28gdGhlcmUgaXMgc29tZSBnb29kIGNvcnJlbGF0aW9uIGJldHdlZW4gbG9nKFNTRCkgYW5kIGxvZyhMRE1DKSBhbmQgYmV0d2VlbiBsb2coU1NEKSBhbmQgbG9nKEhlaWdodCkuDQpsb2coSGVpZ2h0KSBhbmQgbG9nKFNlZWRtYXNzKSBhcmVuJyB0b28gYmFkIGVpdGhlci4NCg0KT2sgbm93IHdlIG5lZWQgYSBwaHlsb2dlbnkgZm9yIHRoZXNlLiBGb3J0dW5hdGVseSBmb3IgdXMsIHRoZSBwYWNrYWdlIGZ1bnNwYWNlIGluY2x1ZGVzIGEgcGh5bG9nZW55IGZvciB0aGVzZSBzcGVjaWVzIFtDYXJtb25hIGV0IGFsLiAyMDIxXShodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL3M0MTU4Ni0wMjEtMDM4NzEteSkuDQoNCmBgYHtyfQ0KcGh5IDwtIGZ1bnNwYWNlOjpwaHlsbw0KYGBgDQoNCiMjIEJyb3duaWFuIG1vdGlvbg0KDQpNb3N0IHRlc3RzIGZvciBwaHlsb2dlbmV0aWMgc2lnbmFsIHJlbHkgb24gQnJvd25pYW4gTW90aW9uIChCTSksIHdoaWNoIGlzIGVxdWl2YWxlbnQgdG8gYSByYW5kb20gd2FsayB0aHJvdWdoIHRpbWUgd2l0aCBhIGtub3duIHZhcmlhbmNlIGBzaWdtYV4yYC4gVGhpcyBtZWFucyB0aGF0IGFzIHRpbWUgaW5jcmVhc2VzLCB0aGUgdmFyaWFuY2UgYWxzbyBpbmNyZWFzZXMgYmV0d2VlbiB0d28gZ2l2ZW4gdGlwcy4NCg0KVHdvIHdpZGVseSB1c2VkIG1ldHJpY3MgZm9yIG1lYXN1cmluZyBwaHlsb2dlbmV0aWMgc2lnbmFsIHVuZGVyIEJNIGFyZSBQYWdlbCdzICRcbGFtYmRhJCBhbmQgQmxvb21iZXJnJ3MgYEtgIChzZWUgUGFnZWwgMTk5OSBhbmQgQmxvb21iZXJnIDIwMDMpLiBUaGVzZSBhcmUgdXN1YWxseSBkZWZpbmVkIGJldHdlZW4gWzAsMV0gc3VjaCBhcyAwIGlzIGNvbXBsZXRlIHJhbmRvbW5lc3MgYW5kIDEgcGVyZmVjdGx5IG1hdGNoZXMgQk0uICRcbGFtYmRhJCBjYW4gZWZmZWN0aXZlbHkgYmUgaGlnaGVyIHRoYW4gMSBidXQgaXMgZ2VuZXJhbGx5IG5vIHdlbGwgZGVmaW5lZCBvdmVyIHRoYXQgbGltaXQuIGBLYCBjYW4gYmUgaGlnaGVyIHRoYW4gMSwgZWZmZWN0aXZlbHkgbWVhbmluZyB0aGVyZSBpcyBzdHJvbmdlciBzaWduYWwgdGhhbiB5b3Ugd291bGQgZXhwZWN0IHVuZGVyIEJNLg0KDQpGb3IgYmluYXJ5IGNhdGVnb3JpY2FsIHRyYWl0cywgdGhlIG9ubHkgbWV0aG9kIEkgYW0gY29uZmlkZW50IGluIHVzaW5nIGlzIHRoZSBwaHlsby5kIG1ldGhvZCBieSBGcml0eiBhbmQgUHVydmlzICgyMDEwKSwgaW1wbGVtZW50ZWQgaW4gYGNhcGVyYC4gQW5vdGhlciBtZXRob2QgaXMgdGhlICRcZGVsdGEkIHN0YXRpc3RpYyBieSBCb3JnZXMgZXQgYWwuIDIwMTkgKGltcGxlbWVudGVkIGhlcmUgaHR0cHM6Ly9naXRodWIuY29tL21yYm9yZ2VzMjMvZGVsdGFfc3RhdGlzdGljL3RyZWUvbWFzdGVyKS4NCg0KTGV0J3Mgc2VlIGlmIGFueSBvZiBvdXIgdHJhaXRzIGNhbiBiZSBtZWFzdXJlZCB3aXRoIHRoZXNlIHR3byBtZXRyaWNzLg0KDQpBcyBhIHJlbWluZGVyLCBJIGFtIHVzaW5nIGxvZyB0cmFuc2Zvcm1lZCB2YXJpYWJsZXMgZm9yIG1vZGVsbGluZy4gVGhpcyBpcyBlYXNpZXIgdG8gZml0IHVuZGVyIEJyb3duaWFuIG1vZGVscyBvZiBldm9sdXRpb24gYW5kIHNvIGlzIG1vcmUgYXBwcm9wcmlhdGUgdG8gdGVzdCBmb3IgcGh5bG9nZW5ldGljIHNpZ25hbCB1bmRlciB0aGVzZSBtb2RlbHMuDQoNCkZpcnN0LCBsZXQncyBtYXRjaCBvdXIgcGh5bG9nZW55IGFuZCB0cmFpdCBkYXRhLiBCZWNhdXNlIHRoZSBkYXRhc2V0IGlzIHF1aXRlIGxhcmdlLCBsZXQncyBzdWJzYW1wbGUgNTAwIHNwZWNpZXMgYW5kIHJ1biBvdXIgYW5hbHlzZXMgb24gdGhpcy4gV2UgY2FuIGV4cGFuZCB0aGUgZGF0YXNldCB0byBzZWUgaG93IHRoZSB0ZXN0cyBwZXJmb3JtIG9uIGEgbGFyZ2VyIGRhdGFzZXQuDQoNClF1aWNrIGZpeCBpbiB0aGUgdHJlZSB0aXAgbGFiZWxzDQoNCmBgYHtyfQ0KcGh5JHRpcC5sYWJlbFs0ODg0XSA8LSAiQ2VyY29jYXJwdXNfbW9udGFudXNfdmFyLl9nbGFiZXIiDQpgYGANCg0KDQpgYGB7ciwgY29tbWVudD1GLCBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgcmVzdWx0cz0naGlkZScsIGVjaG89Rn0NCnNldC5zZWVkKDEyMykNCg0KIyBwaWNhbnRlDQpwaHlsb190cmFpdHMgPC0gbWF0Y2gucGh5bG8uZGF0YShwaHkgPSBwaHksIGRhdGEgPSBuZXdbc2FtcGxlKG5yb3cobmV3KSwgNTAwLCByZXBsYWNlID0gRiksXSkNCmBgYA0KDQpPayBub3cgd2UgY2FuIGNvbXB1dGUgYEtgIGFuZCAkXGxhbWJkYSQgZm9yIGVhY2ggdHJhaXQuIFNvbWUgbW9kZWxzIHdvcmsgYmVzdCB3aGVuIHRoZSB0cmVlcyBhcmUgY29tcGxldGVseSByZXNvbHZlZCAoaWUuIG5vIHBvbHl0b21pZXMpLiBUaHVzIHdlIHdpbGwgbWFrZSBzdXJlIG91ciB0cmVlIGZpdHMgdGhpcyBhc3N1bXB0aW9uLiBJZiBuZWNlc3Nhcnkgd2UgY2FuIHRyYW5zZm9ybSB0aGUgdHJlZSB3aXRoIHBvbHl0b21pZXMgaW50byBkaWNob3RvbWllcyB3aXRoIHplcm8gYnJhbmNoIGxlbmd0aHMgdXNpbmcgdGhlIGhhbmQgYG11bHRpMmRpYCBmdW5jdGlvbi4NCg0KYGBge3J9DQppcy5iaW5hcnkocGh5bG9fdHJhaXRzJHBoeSkNCnBoeWxvX3RyYWl0cyRwaHkgPC0gbXVsdGkyZGkocGh5bG9fdHJhaXRzJHBoeSkNCmBgYA0KR3JlYXQsIG5vdyBsZXQncyBjYWxjdWxhdGUgYEtgIGFuZCAkXGxhbWJkYSQgZm9yIExlYWYgTWFzcyBwZXIgQXJlYS4NCg0KIyMgTE1BDQoNCmBgYHtyfQ0KDQpMTUEgPC0gcGh5bG9fdHJhaXRzJGRhdGEkTE1BICU+JSBzZXROYW1lcyhyb3duYW1lcyhwaHlsb190cmFpdHMkZGF0YSkpDQoNCihLLkxNQTwtcGh5bG9zaWcodHJlZSA9IHBoeWxvX3RyYWl0cyRwaHksIHggPSBMTUEsIG1ldGhvZCA9ICJLIiwgdGVzdCA9IFQsIG5zaW09NTAwKSApDQpwbG90LnBoeWxvc2lnKEsuTE1BKQ0KDQojIFRoaXMgdGFrZXMgYSB2ZXJ5IGxvbmcgdGltZSB0byBjb21wdXRlIGZvciBsYXJnZXIgZGF0YXNldHMgYnV0IHRoZSBnZWlnZXIgZnVuY3Rpb24gYGZpdGNvbnRpbnVvdXNgIHdpdGggbW9kZWwgPSBsYW1iZGEsIHByb3ZpZGVzIHRoZSBzYW1lIHJlc3VsdA0KKGxhbWJkYS5MTUE8LXBoeWxvc2lnKHRyZWUgPSBwaHlsb190cmFpdHMkcGh5LCB4ID0gTE1BLCBtZXRob2QgPSAibGFtYmRhIiwgdGVzdCA9IFQpICkNCmBgYA0KDQpPaywgd2UgY2FuIHNlZSB0aGF0IHRoZXJlIGlzIGEgdmVyeSBsb3cgdmFsdWUgb2YgSywgYW5kIHRoYXQgdGhpcyB2YWx1ZSBpcyBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBmcm9tIHJhbmRvbS4gDQoNCkJ1dCB3ZSBjYW4gYWxzbyBzZWUgdGhhdCB0aGVyZSBpcyBhIG11Y2ggaGlnaGVyIHZhbHVlIGZvciAkXGxhbWJkYSQuDQoNCkhvdyBjYW4gdGhpcyBiZT8gQm90aCBhcmUgc3VwcG9zZWQgdG8gcXVhbnRpZnkgcGh5bG9nZW5ldGljIHNpZ25hbC4gVGhleSBkbyBpbiBmYWN0IGxvb2sgYXQgZGlmZmVyZW50IGFzcGVjdHMgb2YgcGh5bG9nZW5ldGljIHNpZ25hbC4gSyBpcyBhIHJhdGlvIG9mIHdpdGhpbiB0byBiZXR3ZWVuIGNsYWRlIHZhcmlhbmNlLCB3aGlsc3QgbGFtYmRhIGlzIGEgdHJhbnNmb3JtYXRpb24gb2YgdGhlIGRpYWdvbmFsIGVsZW1lbnQgb2YgdGhlIHBWQ1YgbWF0cml4Lg0KDQpUaGUgcC12YWx1ZXMgaGVyZSBjb21wYXJlIHRoZSByZXN1bHRzIHRvIGEgbnVsbCBleHBlY3RhdGlvbiBvZiByYW5kb21uZXNzLCBidXQgdGhlcmUgaXMgbm90IGF1dG9tYXRlZCB3YXkgb2YgY29tcGFyaW5nIHRoaXMgdG8gYSBudWxsIGV4cGVjdGF0aW9uIG9mIEJyb3duaWFuIG1vdGlvbi4gV2UgaGF2ZSB0byBkbyB0aGlzIG1hbnVhbGx5Lg0KDQpgYGB7cn0NCiNwLjkzIG9mIHRoZSBib29rIFBoeWxvZ2VuZXRpYyBDb21wYXJhdGl2ZSBNZXRob2RzIGluIFIgKFJldmVsbCAmIEhhcm1vbiwgMjAyMikNCiNzaW11bGF0ZSAxMDAwIGRhdGFzZXRzDQpudWxsWCA8LSBmYXN0Qk0ocGh5bG9fdHJhaXRzJHBoeSwgbnNpbSA9IDEwMDApDQojIyBmb3IgZWFjaCwgY2Fycnkgb3V0IGEgdGVzdCBvZiBwaHlsb2dlbmV0aWMgc2lnbmFsDQojIyBhbWQgYWNjdW11bGF0ZSB0aGVzZSBpbnRvIGEgdmVjdG9yIHVzaW5nIGFwcGx5DQpudWxsSyA8LSBhcHBseShudWxsWCwyLHBoeWxvc2lnLCB0cmVlPXBoeWxvX3RyYWl0cyRwaHkpDQojY2FsY3VsdGFlIHAudmFsdWVzDQpQdmFsc19MTUEgPC0gbWVhbihudWxsSzw9Sy5MTUEkSykNClB2YWxzX0xNQQ0KYGBgDQoNCkNoZWNrIHRoaXMgb3V0IHZpc3VhbGx5DQoNCmBgYHtyfQ0KaGlzdChjKG51bGxLLCBLLkxNQSRLKSwgYnJlYWtzPTEwMCwgY29sPSJsaWdodGdyYXkiLCBib3JkZXI9ImJsYWNrIiwgbWFpbj0iIiwgeGxhYj0iSyIsIGxhcz0xLCBjZXguYXhpcz0wLjcsIGNleC5sYWI9MC45LCB5bGltPWMoMCwyMDAwKSwgeGxpbT1jKDAsNCkpDQphcnJvd3MoeDA9Sy5MTUEkSywgeTA9cGFyKCkkdXNyWzRdLCB5MT0wLCBsZW5ndGg9MC4xMiwgY29sPW1ha2UudHJhbnNwYXJlbnQoImJsdWUiLCAwLjUpLCBsd2Q9MikNCnRleHQoSy5MTUEkSywgMC45NipwYXIoKSR1c3JbNF0sIHBhc3RlKCJvYnNlcnZlZCB2YWx1ZSBvZiBLIChQPSAgIiwgcm91bmQoUHZhbHNfTE1BLCA0KSwgIikiLCBzZXAgPSAiIiksIHBvcz00LCBjZXg9MC44KQ0KYGBgDQpXZSBjYW4gc2VlIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBLIHVuZGVyIEJyb3duaWFuIG1vdGlvbiBpcyB2ZXJ5IHdpZGVzcHJlYWQhIEJ1dCBvdXIgdmFsdWUgb2YgSyBpcyBpbmRlZWQgbG93ZXIgdGhhbiBleHBlY3RlZCB1bmRlciBCcm93bmlhbiBtb3Rpb24uDQoNCldoYXQgYWJvdXQgdGhlIHZhbHVlIG9mIGxhbWJkYT8gQ2FuIHdlIHJlamVjdCBhIG51bGwgb2YgbGFtYmRhID0gMT8NCg0KV2UgY2FuIHVzZSBhIGxpa2VsaWhvb2QgcmF0aW8gdGVzdCBmb3IgdGhpcyBvbmUuDQoNCmBgYHtyfQ0KKExSX0xNQSA8LSAtMioobGFtYmRhLkxNQSRsaWsoMSkgLSBsYW1iZGEuTE1BJGxvZ0wpICkNCmBgYA0KDQpOb3cgY29tcHV0ZSB0aGUgcC12YWx1ZSAoYXNzdW1pbmcgY2hpLXNxdWFyZSBkaXN0cmlidXRpb24gdW5kZXIgdGhlIG51bGwgaHlwb3RoZXNpcyBvZiBsYW1iZGEgPSAxKQ0KDQpgYGB7cn0NClB2YWxfbGFtYmRhX0xNQSA8LSBwY2hpc3EoTFJfTE1BLCBkZj0xLCBsb3dlci50YWlsID0gRiApDQpQdmFsX2xhbWJkYV9MTUENCmBgYA0KDQpTbyB3ZSBjYW4gcmVqZWN0IHRoZSBudWxsIHRoYXQgJFxsYW1iZGEkID0gMS4NCg0KVGhpcyBtZWFucyB0aGF0IGJvdGggYEtgIGFuZCAkXGxhbWJkYSQgYXJlIHRlbGxpbmcgdXMgdGhlcmUgaXMgbW9yZSBwaHlsb2dlbmV0aWMgc2lnbmFsIHRoYW4gcmFuZG9tLCBidXQgbGVzcyB0aGFuIGV4cGVjdGVkIHVuZGVyIEJyb3duaWFuIG1vdGlvbi4gU28gcGVyaGFwcyBvdmVyZGlzcGVyc2VkID8NCg0KV2UgY2FuIGFsc28gY29tcGFyZSB0aGVzZSByZXN1bHRzIHRvIG90aGVyIG1vZGVscyBvZiBldm9sdXRpb24sIGxpa2UgRWFybHktQnVyc3QgKEVCKSBvciBPcm5zdGVpbi1VaGxlbmJlY2sgKE9VKSBtb2RlbHMuDQoNCmBgYHtyfQ0KKEJNLkxNQSA8LSBmaXRDb250aW51b3VzKHBoeWxvX3RyYWl0cyRwaHksIExNQSwgbW9kZWwgPSAiQk0iKSApDQoNCihFQi5MTUEgPC0gZml0Q29udGludW91cyhwaHlsb190cmFpdHMkcGh5LCBMTUEsIG1vZGVsID0gIkVCIikgKQ0KDQooT1UuTE1BIDwtIGZpdENvbnRpbnVvdXMocGh5bG9fdHJhaXRzJHBoeSwgTE1BLCBtb2RlbCA9ICJPVSIpICkNCg0KKExhbWJkYS5MTUEgPC0gZml0Q29udGludW91cyhwaHlsb190cmFpdHMkcGh5LCBMTUEsIG1vZGVsID0gImxhbWJkYSIpICkNCmBgYA0KDQpJdCBzZWVtcyBsaWtlIHRoZSBgYWAgcGFyYW1ldGVyIGZvciB0aGUgRUIgbW9kZWwgaXMgY2xvc2UgdG8gMCwgd2hpY2ggaXMgZWZmZWN0aXZlbHkgZXF1aXZhbGVudCB0byBhIEJNLg0KDQpJZiB3ZSBjaGVjayB0aGUgb3V0cHV0IGZvciB0aGUgbGFtYmRhIG1vZGVsLCB0aGlzIGlzIHRoZSBzYW1lIGFzIHVzaW5nIHRoZSBgcGh5bG9zaWdgIGZ1bmN0aW9uIGZyb20gcGh5dG9vbHMgYnV0IGRvZXMgaXQgbXVjaCBxdWlja2VyIGFuZCB3aXRoIGEgZmV3IG1vcmUgYml0cyBvZiBpbmZvcm1hdGlvbi4NCg0KDQpgYGB7cn0NCiNIb3dldmVyIGlmIHRoZSBPVSBtb2RlbCBzZWVtcyB0byBiZSBhdCBib3VuZHMgd2l0aCB0aGUgYGFscGhhYCBwYXJhbWV0ZXIsIHRoZW4gd2UgY2FuIGluY3JlYXNlIHRoaXMgYW5kIHNlZSBob3cgdGhlIG1vZGVsIHBlcmZvcm1zLg0KDQojKE9VLkxNQSA8LSBmaXRDb250aW51b3VzKHBoeWxvX3RyYWl0cyRwaHksIExNQSwgbW9kZWwgPSAiT1UiLCBib3VuZHMgPSBsaXN0KGFscGhhPWMoMCw1MCkpKSApDQpgYGANCg0KSXQncyBhbHNvIGdvb2QgdG8gc2VlIGhvdyB0aGVzZSBjb21wYXJlIHRvIHJhbmRvbSBub2lzZSBtb2RlbC4gYHdoaXRlYCBpcyBhIHdoaXRlLW5vaXNlIChub24tcGh5bG9nZW5ldGljKSBtb2RlbCwgd2hpY2ggYXNzdW1lcyBkYXRhIGNvbWUgZnJvbSBhIHNpbmdsZSBub3JtYWwgZGlzdHJpYnV0aW9uIHdpdGggbm8gY292YXJpYW5jZSBzdHJ1Y3R1cmUgYW1vbmcgc3BlY2llcy4gVGhlIHZhcmlhbmNlIHBhcmFtZXRlciBzaWdzcSB0YWtlcyB0aGUgc2FtZSBib3VuZHMgZGVmaW5lZCB1bmRlciB0aGUgQk0gbW9kZWwuDQoNCg0KYGBge3J9DQpudWxsLkxNQSA8LSBmaXRDb250aW51b3VzKHBoeSA9IHBoeWxvX3RyYWl0cyRwaHksIGRhdCA9IExNQSwgbW9kZWwgPSAid2hpdGUiKQ0KYGBgDQoNCg0KTm93IHdlIGNhbiBjb21wYXJlIGhvdyB3ZWxsIGVhY2ggbW9kZWwgcGVyZm9ybXMgdXNpbmcgQUlDDQoNCmBgYHtyfQ0KKGFpY3NfbW9kZWxzIDwtIHNldE5hbWVzKGMoQUlDKEJNLkxNQSksIEFJQyhFQi5MTUEpLCBBSUMoT1UuTE1BKSwgQUlDKG51bGwuTE1BKSksIGMoIkJNIiwgIkVCIiwgIk9VIiwgIk5VTEwiKSkgKQ0KYGBgDQoNClNlZW1zIGxpa2UgdGhlIE9VIG1vZGVsIGhhcyB0aGUgbG93ZXN0IEFJQy4gV2UgY2FuIGFsc28gY2hlY2sgdGhlIHdlaWdodHMuDQoNCmBgYHtyfQ0KYWljLncoYWljc19tb2RlbHMpDQpgYGANCg0KV2hhdCBhYm91dCBjb21wYXJpbmcgT1UgdG8gTGFtYmRhIG1vZGVsID8NCg0KYGBge3J9DQphaWMudyhzZXROYW1lcyhjKEFJQyhPVS5MTUEpLCBBSUMoTGFtYmRhLkxNQSkpLCBjKCJPVSIsICJMYW1iZGEiKSkgKQ0KYGBgDQoNClNvIGluIHRoaXMgY2FzZSB0aGUgbGFtYmRhIHRyYW5zZm9ybWF0aW9uIGZpdHMgdGhlIGRhdGEgYmV0dGVyLg0KDQpXZSBjYW4gY2hlY2sgdGhpcyBvdXQgdmlzdWFsbHkgYW5kIGFwcGx5IHRoZSBsYW1iZGEgYW5kIE9VIHRyYW5zZm9ybWF0aW9ucy4NCg0KYGBge3J9DQojbm9ybWFsIHRyZWUNCmNvbnRNYXAodHJlZSA9IHBoeWxvX3RyYWl0cyRwaHksIHggPSBMTUEsIGZ0eXBlID0gIm9mZiIsIHR5cGUgPSAiZmFuIiwgbHdkPTIpDQoNCiNsYW1iZGEgdHJhbnNmb3JtYXRpb24NCiNjb250TWFwKHRyZWUgPSBsYW1iZGFUcmVlKHBoeWxvX3RyYWl0cyRwaHksbGFtYmRhID0gTGFtYmRhLkxNQSRvcHQkbGFtYmRhKSwgeCA9IExNQSwgZnNpemUgPSAwLjcsIGZ0eXBlID0gIm9mZiIsIHR5cGUgPSAiZmFuIiwgbHdkPTIpDQojIG9yIHVzZSB0aGlzDQpjb250TWFwKHRyZWUgPSByZXNjYWxlKHBoeWxvX3RyYWl0cyRwaHksIG1vZGVsID0gImxhbWJkYSIsIGxhbWJkYSA9IExhbWJkYS5MTUEkb3B0JGxhbWJkYSksIHggPSBMTUEsIGZ0eXBlID0gIm9mZiIsIHR5cGUgPSAiZmFuIiwgbHdkPTIpDQoNCiNPVSB0cmFuc2Zvcm1hdGlvbg0KY29udE1hcCh0cmVlID0gcmVzY2FsZShwaHlsb190cmFpdHMkcGh5LCBtb2RlbCA9ICJPVSIsIGFscGhhPU9VLkxNQSRvcHQkYWxwaGEpLCB4ID0gTE1BLCBmdHlwZSA9ICJvZmYiLCB0eXBlID0gImZhbiIsIGx3ZD0yKQ0KYGBgDQoNClRoZSBPVSBtb2RlbCBzZWVtcyBxdWl0ZSBzdHJhbmdlLiBJdCBsb29rcyBsaWtlIG1vc3QgYnJhbmNoZXMgaGF2ZSBiZWVuIHNob3J0ZW5lZCBzbyBtdWNoIHRoZXkgYXJlIGFsbW9zdCBsaWtlIGEgc3RhciBwaHlsb2dlbnkuDQoNCldlIGNhbiBkbyB0aGlzIGZvciBhbGwgdGhlIHRyYWl0cyBvbmUgYnkgb25lIGFuZCBzZWUgaG93IG11Y2ggcGh5bG9nZW5ldGljIHNpZ25hbCB0aGV5IGhhdmUuDQoNCg0KYGBge3J9DQpTU0QgPC0gcGh5bG9fdHJhaXRzJGRhdGEkU1NEICU+JSBzZXROYW1lcyhyb3duYW1lcyhwaHlsb190cmFpdHMkZGF0YSkpDQpIZWlnaHQgPC0gcGh5bG9fdHJhaXRzJGRhdGEkSGVpZ2h0ICU+JSBzZXROYW1lcyhyb3duYW1lcyhwaHlsb190cmFpdHMkZGF0YSkpDQpMQSA8LSBwaHlsb190cmFpdHMkZGF0YSRMQSU+JSBzZXROYW1lcyhyb3duYW1lcyhwaHlsb190cmFpdHMkZGF0YSkpDQpMRE1DIDwtIHBoeWxvX3RyYWl0cyRkYXRhJExETUMgJT4lIHNldE5hbWVzKHJvd25hbWVzKHBoeWxvX3RyYWl0cyRkYXRhKSkgIyBoYXMgc29tZSBOQSB2YWx1ZXMNCk5tYXNzIDwtIHBoeWxvX3RyYWl0cyRkYXRhJE5tYXNzICU+JSBzZXROYW1lcyhyb3duYW1lcyhwaHlsb190cmFpdHMkZGF0YSkpDQpTZWVkbWFzcyA8LSBwaHlsb190cmFpdHMkZGF0YSRTZWVkbWFzcyAlPiUgc2V0TmFtZXMocm93bmFtZXMocGh5bG9fdHJhaXRzJGRhdGEpKQ0KYGBgDQoNCk5vdyBmaXQgdGhlIG1vZGVscw0KDQojIyBTU0QNCg0KYGBge3J9DQooSy5TU0Q8LXBoeWxvc2lnKHRyZWUgPSBwaHlsb190cmFpdHMkcGh5LCB4ID0gU1NELCBtZXRob2QgPSAiSyIsIHRlc3QgPSBULCBuc2ltPTUwMCkgKQ0KcGxvdC5waHlsb3NpZyhLLlNTRCkNCg0KKExhbWJkYS5TU0QgPC0gZml0Q29udGludW91cyhwaHlsb190cmFpdHMkcGh5LCBTU0QsIG1vZGVsID0gImxhbWJkYSIpICkNCg0KKEJNLlNTRCA8LSBmaXRDb250aW51b3VzKHBoeWxvX3RyYWl0cyRwaHksIFNTRCwgbW9kZWwgPSAiQk0iKSApDQoNCihFQi5TU0QgPC0gZml0Q29udGludW91cyhwaHlsb190cmFpdHMkcGh5LCBTU0QsIG1vZGVsID0gIkVCIikgKQ0KDQooT1UuU1NEIDwtIGZpdENvbnRpbnVvdXMocGh5bG9fdHJhaXRzJHBoeSwgU1NELCBtb2RlbCA9ICJPVSIpICkNCg0KKG51bGwuU1NEIDwtIGZpdENvbnRpbnVvdXMocGh5bG9fdHJhaXRzJHBoeSwgU1NELCBtb2RlbCA9ICJ3aGl0ZSIpKQ0KYGBgDQoNCkhlcmUgJFxsYW1iZGEkIGlzIDAuOTYsIHNvIHByZXR0eSBzdHJvbmcNCg0KQ2hlY2sgd2hpY2ggZml0cyBiZXN0IGFuZCB0aGUgQUlDIHdlaWdodHMNCg0KYGBge3J9DQooYWljc19tb2RlbHMgPC0gc2V0TmFtZXMoYyhBSUMoQk0uU1NEKSwgQUlDKEVCLlNTRCksIEFJQyhPVS5TU0QpLCBBSUMobnVsbC5TU0QpKSwgYygiQk0iLCAiRUIiLCAiT1UiLCAiTlVMTCIpKSApDQoNCmFpYy53KGFpY3NfbW9kZWxzKQ0KYGBgDQoNCkFnYWluLCBPVSBzZWVtcyB0byBkbyBiZXN0Lg0KDQojIyBObWFzcw0KDQpgYGB7cn0NCihLLk5tYXNzPC1waHlsb3NpZyh0cmVlID0gcGh5bG9fdHJhaXRzJHBoeSwgeCA9IE5tYXNzLCBtZXRob2QgPSAiSyIsIHRlc3QgPSBULCBuc2ltPTUwMCkgKQ0KcGxvdC5waHlsb3NpZyhLLk5tYXNzKQ0KDQooTGFtYmRhLk5tYXNzIDwtIGZpdENvbnRpbnVvdXMocGh5bG9fdHJhaXRzJHBoeSwgTm1hc3MsIG1vZGVsID0gImxhbWJkYSIpICkNCg0KKEJNLk5tYXNzIDwtIGZpdENvbnRpbnVvdXMocGh5bG9fdHJhaXRzJHBoeSwgTm1hc3MsIG1vZGVsID0gIkJNIikgKQ0KDQooRUIuTm1hc3MgPC0gZml0Q29udGludW91cyhwaHlsb190cmFpdHMkcGh5LCBObWFzcywgbW9kZWwgPSAiRUIiKSApDQoNCihPVS5ObWFzcyA8LSBmaXRDb250aW51b3VzKHBoeWxvX3RyYWl0cyRwaHksIE5tYXNzLCBtb2RlbCA9ICJPVSIpICkNCg0KKG51bGwuTm1hc3MgPC0gZml0Q29udGludW91cyhwaHlsb190cmFpdHMkcGh5LCBObWFzcywgbW9kZWwgPSAid2hpdGUiKSkNCmBgYA0KDQpIZXJlIEsgc2VlbXMgdG8gYmUgbm9uLXNpZ25pZmljYW50LiBMYW1iZGEgaXMgbW9kZXJhdGUgdG8gaGlnaC4NCg0KQ2hlY2sgQUlDIHdlaWdodHMNCg0KYGBge3J9DQooYWljc19tb2RlbHMgPC0gc2V0TmFtZXMoYyhBSUMoQk0uTm1hc3MpLCBBSUMoRUIuTm1hc3MpLCBBSUMoT1UuTm1hc3MpLCBBSUMobnVsbC5ObWFzcykpLCBjKCJCTSIsICJFQiIsICJPVSIsICJOVUxMIikpICkNCg0KYWljLncoYWljc19tb2RlbHMpDQpgYGANCg0KVGhpcyB0aW1lIGEgbnVsbCBtb2RlbCBvZiB3aGl0ZSBub2lzZSAoZ3JhbmQgbWVhbikgaXMgYmVzdC4gDQoNCiMjIEhlaWdodA0KDQpgYGB7cn0NCihLLkhlaWdodDwtcGh5bG9zaWcodHJlZSA9IHBoeWxvX3RyYWl0cyRwaHksIHggPSBIZWlnaHQsIG1ldGhvZCA9ICJLIiwgdGVzdCA9IFQsIG5zaW09NTAwKSApDQpwbG90LnBoeWxvc2lnKEsuSGVpZ2h0KQ0KDQooTGFtYmRhLkhlaWdodCA8LSBmaXRDb250aW51b3VzKHBoeWxvX3RyYWl0cyRwaHksIEhlaWdodCwgbW9kZWwgPSAibGFtYmRhIikgKQ0KDQooQk0uSGVpZ2h0IDwtIGZpdENvbnRpbnVvdXMocGh5bG9fdHJhaXRzJHBoeSwgSGVpZ2h0LCBtb2RlbCA9ICJCTSIpICkNCg0KKEVCLkhlaWdodCA8LSBmaXRDb250aW51b3VzKHBoeWxvX3RyYWl0cyRwaHksIEhlaWdodCwgbW9kZWwgPSAiRUIiKSApDQoNCihPVS5IZWlnaHQgPC0gZml0Q29udGludW91cyhwaHlsb190cmFpdHMkcGh5LCBIZWlnaHQsIG1vZGVsID0gIk9VIikgKQ0KDQoobnVsbC5IZWlnaHQgPC0gZml0Q29udGludW91cyhwaHlsb190cmFpdHMkcGh5LCBIZWlnaHQsIG1vZGVsID0gIndoaXRlIikpDQpgYGANCg0KTGFtYmRhIGlzIHByZXR0eSBtdWNoIGVxdWFsIHRvIDENCg0KQ2hlY2sgQUlDIHdlaWdodHMNCg0KYGBge3J9DQooYWljc19tb2RlbHMgPC0gc2V0TmFtZXMoYyhBSUMoQk0uSGVpZ2h0KSwgQUlDKEVCLkhlaWdodCksIEFJQyhPVS5IZWlnaHQpLCBBSUMobnVsbC5IZWlnaHQpKSwgYygiQk0iLCAiRUIiLCAiT1UiLCAiTlVMTCIpKSApDQoNCmFpYy53KGFpY3NfbW9kZWxzKQ0KYGBgDQpPVSBtb2RlbCBmaXRzIGRhdGEgYmVzdA0KDQojIyBMQQ0KDQpgYGB7cn0NCihLLkxBPC1waHlsb3NpZyh0cmVlID0gcGh5bG9fdHJhaXRzJHBoeSwgeCA9IExBLCBtZXRob2QgPSAiSyIsIHRlc3QgPSBULCBuc2ltPTUwMCkgKQ0KcGxvdC5waHlsb3NpZyhLLkxBKQ0KDQooTGFtYmRhLkxBIDwtIGZpdENvbnRpbnVvdXMocGh5bG9fdHJhaXRzJHBoeSwgTEEsIG1vZGVsID0gImxhbWJkYSIpICkNCg0KKEJNLkxBIDwtIGZpdENvbnRpbnVvdXMocGh5bG9fdHJhaXRzJHBoeSwgTEEsIG1vZGVsID0gIkJNIikgKQ0KDQooRUIuTEEgPC0gZml0Q29udGludW91cyhwaHlsb190cmFpdHMkcGh5LCBMQSwgbW9kZWwgPSAiRUIiKSApDQoNCihPVS5MQSA8LSBmaXRDb250aW51b3VzKHBoeWxvX3RyYWl0cyRwaHksIExBLCBtb2RlbCA9ICJPVSIpICkNCg0KKG51bGwuTEEgPC0gZml0Q29udGludW91cyhwaHlsb190cmFpdHMkcGh5LCBMQSwgbW9kZWwgPSAid2hpdGUiKSkNCmBgYA0KDQoNCkNoZWNrIEFJQyB3ZWlnaHRzDQoNCmBgYHtyfQ0KKGFpY3NfbW9kZWxzIDwtIHNldE5hbWVzKGMoQUlDKEJNLkxBKSwgQUlDKEVCLkxBKSwgQUlDKE9VLkxBKSwgQUlDKG51bGwuTEEpKSwgYygiQk0iLCAiRUIiLCAiT1UiLCAiTlVMTCIpKSApDQoNCmFpYy53KGFpY3NfbW9kZWxzKQ0KYGBgDQoNCkFnYWluLCBPVQ0KDQojIyBMRE1DDQoNCmBgYHtyfQ0KKEsuTERNQzwtcGh5bG9zaWcodHJlZSA9IHBoeWxvX3RyYWl0cyRwaHksIHggPSBMRE1DWyFpcy5uYShMRE1DKV0sIG1ldGhvZCA9ICJLIiwgdGVzdCA9IFQsIG5zaW09NTAwKSApDQpwbG90LnBoeWxvc2lnKEsuTERNQykNCg0KKExhbWJkYS5MRE1DIDwtIGZpdENvbnRpbnVvdXMocGh5bG9fdHJhaXRzJHBoeSwgTERNQ1shaXMubmEoTERNQyldLCBtb2RlbCA9ICJsYW1iZGEiKSApDQoNCihCTS5MRE1DIDwtIGZpdENvbnRpbnVvdXMocGh5bG9fdHJhaXRzJHBoeSwgTERNQ1shaXMubmEoTERNQyldLCBtb2RlbCA9ICJCTSIpICkNCg0KKEVCLkxETUMgPC0gZml0Q29udGludW91cyhwaHlsb190cmFpdHMkcGh5LCBMRE1DWyFpcy5uYShMRE1DKV0sIG1vZGVsID0gIkVCIikgKQ0KDQooT1UuTERNQyA8LSBmaXRDb250aW51b3VzKHBoeWxvX3RyYWl0cyRwaHksIExETUNbIWlzLm5hKExETUMpXSwgbW9kZWwgPSAiT1UiKSApDQoNCihudWxsLkxETUMgPC0gZml0Q29udGludW91cyhwaHlsb190cmFpdHMkcGh5LCBMRE1DWyFpcy5uYShMRE1DKV0sIG1vZGVsID0gIndoaXRlIikpDQpgYGANCg0KTW9kZXJhdGUgc2lnbmFsDQoNCkNoZWNrIEFJQyB3ZWlnaHRzDQoNCmBgYHtyfQ0KKGFpY3NfbW9kZWxzIDwtIHNldE5hbWVzKGMoQUlDKEJNLkhlaWdodCksIEFJQyhFQi5IZWlnaHQpLCBBSUMoT1UuSGVpZ2h0KSwgQUlDKG51bGwuSGVpZ2h0KSksIGMoIkJNIiwgIkVCIiwgIk9VIiwgIk5VTEwiKSkgKQ0KDQphaWMudyhhaWNzX21vZGVscykNCmBgYA0KQWdhaW4sIE9VDQoNCg0KDQpTbyBvdmVyYWxsICwgdGhlIE9ybnN0ZWluLVVobGVuYmVjayBtb2RlbCBzZWVtcyB0byBiZSB0aGUgYmVzdCBtb2RlbCBmb3IgbW9zdCBvZiBvdXIgZGF0YSwgZXhjZXB0IE5tYXNzIHdoaWNoIHZhcmllcyBhbG9uZyBhIGdyYW5kIG1lYW4gKHdoaXRlLW5vaXNlKS4NCg0KIyBDYXRlZ29yaWNhbCB0cmFpdHMNCg0KQ2hlY2sgdGhlIGNhdGVnb3JpY2FsIHRyYWl0IGBHcm93dGggRm9ybWANCg0KV2Ugd2lsbCB1c2UgdGhlICRcZGVsdGEkIHN0YXRpc3RpYyBmb3IgdGhpcy4NCg0KYGBge3J9DQpzb3VyY2UoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9tcmJvcmdlczIzL2RlbHRhX3N0YXRpc3RpYy9tYXN0ZXIvY29kZS5SIikNCg0KdHJhaXQgPC0gZmlsdGVyZWRfZGF0YSAlPiUgZHBseXI6OnNlbGVjdChTcGVjaWVzLCBgR3Jvd3RoIEZvcm1gKSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJTcGVjaWVzIikgJT4lIC5bcm93bmFtZXMocGh5bG9fdHJhaXRzJGRhdGEpLF0NCg0KIyBUaGlzIHRha2VzIGEgdmVyeSBsb25nIHRpbWUsIGdvIGZvciBsdW5jaCBvciBzb21ldGhpbmcgLi4uDQojZGVsdGFBIDwtIGRlbHRhKHRyYWl0LHBoeWxvX3RyYWl0cyRwaHksMC4xLDAuMDU4OSwxMDAwMCwxMCwxMDApDQpgYGANCg0KQW5kIGNoZWNrIHRoZSBwLXZhbHVlIGJ5IHVzaW5nIHJhbmRvbWl6YXRpb24uDQoNClRoaXMgY2FuIHRha2UgYSB3aGlsZSBhZ2FpbiAuLi4NCg0KYGBge3J9DQojIHJhbmRvbV9kZWx0YSA8LSByZXAoTkEsMTAwKQ0KIyBmb3IgKGkgaW4gMToxMDApew0KIyAgIHJ0cmFpdCA8LSBzYW1wbGUodHJhaXQpDQojICAgcmFuZG9tX2RlbHRhW2ldIDwtIGRlbHRhKHJ0cmFpdCxwaHlsb190cmFpdHMkcGh5LDAuMSwwLjA1ODksMTAwMDAsMTAsMTAwKQ0KIyB9DQojIHBfdmFsdWUgPC0gc3VtKHJhbmRvbV9kZWx0YT5kZWx0YUEpL2xlbmd0aChyYW5kb21fZGVsdGEpDQojIGJveHBsb3QocmFuZG9tX2RlbHRhKQ0KIyBhYmxpbmUoaD1kZWx0YUEsY29sPSJyZWQiKQ0KYGBgDQoNCkZvciBiaW5hcnkgZGF0YSB5b3UgY2FuIHVuY29tbWVudCB0aGUgY29kZSBiZWxsb3cNCg0KYGBge3J9DQojIGNhcGVyIGlzIGEgYml0IGZ1c3N5IGFib3V0IHRoZSBmb3JtYXQgb2YgdGhlIGRhdGENCiMgd2UgbmVlZCB0byBwcm92aWRlIGEgdHJlZSB3aXRoIG5vIG5vZGUgbmFtZXMNCiMgY2FwZXIudHJlZSA8LSBwaHlsb190cmFpdHMkcGh5DQojIGNhcGVyLnRyZWUkbm9kZS5sYWJlbCA8LSBOVUxMDQojIA0KIyBjb21wLmRhdGEgPC0gY29tcGFyYXRpdmUuZGF0YShjYXBlci50cmVlLCB5b3VyZGF0YWZyYW1lX3dpdGhfY29sdW1uX29mX2ludGVyZXN0ICwgU3BlY2llcykNCiMgDQojIGNvbXAuZGF0YQ0KIyANCiMgRCA8LSBwaHlsby5kKGNvbXAuZGF0YSwgYmludmFyID0gR3Jvd3RoKQ0KYGBgDQoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBDaG9zaW5nIGltcHV0YXRpb24gbWV0aG9kDQoNClNvIG5vdyB0aGF0IHdlIGhhdmUgYW4gaWRlYSBvZiB0aGUgc3RyZW5ndGggb2YgdGhlIHBoeWxvZ2VuZXRpYyBzaWduYWwsIGl0J3MgdGltZSB0byBkZWNpZGUgb24gYSBtZXRob2Qgb2YgaW1wdXRhdGlvbi4NCg0KQ3VycmVudGx5IHRoZXJlIGFyZSAzIG1haW4gZmxhdm91cnMgb2YgcGh5bG9nZW5ldGljIGltcHV0YXRpb24gW3NlZSByZXZpZXcgaW4gTW9sbmEtVmVuZWdhcyBldCBhbC4gMjAxOF0oaHR0cHM6Ly93d3cuY2ljYS5lcy93cC1jb250ZW50L3VwbG9hZHMvMjAxOC8wMy9Nb2xpbmEtVmVuZWdhc18yMDE4X0ltcHV0YXRpb24ucGRmKToNCg0KICAtIHBoeWxvZ2VuZXRpYyBnZW5lcmFsaXplZCBsaW5lYXIgbW9kZWxzIChwR0xNOyBTd2Vuc29uIDIwMTQsIEdvb2xzYnkgZXQgYWwuIDIwMTZhKQ0KICAtIHBoeWxvZ2VuZXRpYyBlaWdlbnZlY3RvciByZWdyZXNzaW9uIG1vZGVscyAoUFZSOyBEaW5pekZpbGhvIGV0IGFsLiAxOTk4KSANCiAgLSBwaHlsb2dlbmV0aWMgZWlnZW52ZWN0b3IgbWFwcyAoUEVNOyBHdcOpbmFyZCBldCBhbC4gMjAxMykNCiAgDQpwR0xNIGNhbiBlYXNpbHkgYmUgaW1wbGVtZW50ZWQgYnkgZml0dGluZyBhIHBWQ1YgbWF0cml4IGFzIHRoZSBjb3JyZWxhdGlvbiBzdHJ1Y3R1cmUgaW4gYSBnbHMgbW9kZWwgKHBhY2thZ2UgYG5sbWVgKS4gVGhpcyBhc3N1bWVzIGEgQnJvd25pYW4gbW9kZWwgb2YgZXZvbHV0aW9uLiBNb3JlIHNvcGhpc3RpY2F0ZWQgd2F5cyBvZiBpbXBsZW1lbnRpbmcgYSBwR0xNIGNhbiBiZSBkb25lIHdpdGggdGhlIHBhY2thZ2UgYHJQaHlsb3BhcnNgIGFuZCBzZXZlcmFsIGV2b2x1dGlvbmFyeSBtb2RlbHMgY2FuIGJlIGZpdHRlZC4gU2VlIHR1dG9yaWFsIFtoZXJlXShodHRwczovL2Jlc2pvdXJuYWxzLm9ubGluZWxpYnJhcnkud2lsZXkuY29tL2FjdGlvbi9kb3dubG9hZFN1cHBsZW1lbnQ/ZG9pPTEwLjExMTElMkYyMDQxLTIxMFguMTI2MTImZmlsZT1tZWUzMTI2MTItc3VwLTAwMDMtQXBwZW5kaXhTMy5wZGYpDQoNClBWUiBjYW4gYmUgZml0IHVzaW5nIGEgcmFuZG9tRm9yZXN0IGFwcHJvYWNoIGluIHRoZSBwYWNrYWdlIGBtaXNzRm9yZXN0YC4gT25lIHNpbXBseSBuZWVkcyB0byB0cmFuc2Zvcm0gdGhlIHBoeWxvZ2VueSBpbnRvIGEgcGh5bG9nZW5ldGljIGRpc3RhbmNlIG1hdHJpeCBhbmQgdXNlIHRoaXMgaW4gYSBQQ29BLiBUaGVuIHRoZSBQQ29BIGF4ZXMgYXJlIGFkZGVkIGFzIGV4dHJhIHZhcmlhYmxlcyB0byB0aGUgdHJhaXQgbWF0cml4IGFzIHByZWRpY3RvcnMuDQoNClBFTSBjYW4gYmUgZml0IHVzaW5nIHRoZSBSIHBhY2thZ2UgYE1QU0VNYC4gVGhpcyBleHBhbmRzIG9uIHRoZSBQVlIgbWV0aG9kIGJ5IGFkZGluZyBhbiBldm9sdXRpb25hcnkgbW9kZWwgYW5kIGlzIHRodXMgY29uc2lkZXJlZCBtb3JlIGFwcHJvcHJpYXRlIGZvciBldm8tZWNvIG1vZGVsbGluZy4gVGhlIHRyZWUgaXMgdHJhbnNmb3JtZWQgcHJpb3IgdG8gdHJhbnNmb3JtaW5nIGludG8gZGlzdGFuY2UgbWF0cml4IGFuZCBQQ29BLg0KDQpBIGZvdXJ0aCBvcHRpb24gd291bGQgYmUgdG8gdXNlIEJheWVzaWFuIEhpZXJhcmNoaWNhbCBNYXRyaXggRmFjdG9yaXphdGlvbiAoQkhQTUYpLCB3aGljaCBoYXMgYmVlbiB1c2VkIGJ5IG1hbnkgcHVibGljYXRpb25zIGZvciB0cmFpdCBpbXB1dGF0aW9uLiBUaGlzIG1ldGhvZCBob3dldmVyIGRvZXNuJ3QgcmVhbGx5IHVzZSB0aGUgcGh5bG9nZW55IG9yIHF1YW50aWZ5IHBoeWxvZ2VuZXRpYyBzaWduYWwuIEl0IHVzZXMgYSB0YXhvbm9taWMgaGllcmFyY2h5IGFuZCBhc3N1bWVzIHRoYXQgYWxsIHJhbmtzIGFyZSBlcXVhbCBhbmQgY29udGFpbiBpbmZvcm1hdGlvbi4gSW4gcmVhbGl0eSwgYWxsIHRheG9ub21pYyByYW5rcyBhcmUgbm90IGVxdWFsLCBhcyBzb21lIGdlbmVyYSBjYW4gYmUgb2xkZXIgdGhhbiBjZXJ0YWluIGZhbWlsaWVzIGZvciBleGFtcGxlLiBBcyB3aXRoIGFsbCBCYXllc2lhbiBtb2RlbHMsIGl0IHdpbGwgdXN1YWxseSBiZSBtdWNoIHNsb3dlciBidXQgd2lsbCBwcm92aWRlIHVuY2VydGFpbnR5IG9mIHRoZSBwcmVkaWN0aW9uLg0KDQojIyBwR0xNDQoNCkxldCdzIHNlZSBhIHNpbXBsZSBleGFtcGxlIHdpdGggcEdMTQ0KDQpGaXJzdCwgbGV0J3MgcmFuZG9tbHkgcmVtb3ZlIDEwJSBvZiBhbGwgdmFsdWVzIGluIHRoZSBkYXRhIGZyYW1lLiBUaGUgYG1pc3NGb3Jlc3RgIHBhY2thZ2UgY2FuIG1ha2UgdGhpcyBlYXN5IGZvciB1cyB1c2luZyB0aGUgYHByb2ROQWAgZnVuY3Rpb24uDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoNDMxMikNCm1pc3NpbmcgPC0gcGh5bG9fdHJhaXRzJGRhdGFbLC0xXSAlPiUgcHJvZE5BKDAuMSkNCg0KaGVhZChtaXNzaW5nKQ0KYGBgDQoNCg0KYFJQaHlsb3BhcnNgIGlzIGEgYml0IGZ1c3N5IGFuZCByZXF1aXJlcyBhIGBzcGVjaWVzYCBjb2x1bW4gKGFsbCBsb3dlciBjYXNlKSB0aGF0IGlzIHRoZSB2ZXJ5IGZpcnN0IGNvbHVtbiBpbiB0aGUgdHJhaXQgbWF0cml4Lg0KDQpgYGB7cn0NCm1pc3NpbmcgPC0gbWlzc2luZyAlPiUgDQogIG11dGF0ZShzcGVjaWVzID0gcm93bmFtZXMoLikpICU+JSANCiAgZHBseXI6OnNlbGVjdChzcGVjaWVzLCBldmVyeXRoaW5nKCkpDQoNCm1vZGVsLkJNIDwtIHBoeWxvcGFycyh0cmVlID0gcGh5bG9fdHJhaXRzJHBoeSwgdHJhaXRfZGF0YSA9IG1pc3NpbmcsIG1vZGVsID0gIkJNIikNCm1vZGVsLkJNDQpzdW1tYXJ5KG1vZGVsLkJNKQ0KYGBgDQoNCldlIGNhbiBjb21wYXJlIHdpdGggb3RoZXIgbW9kZWxzLiBFQiwgT1UsIGxhbWJkYSBhbmQgdHdvIHZhcmlhbnRzIG9mIG11bHRpdmFyaWF0ZS1PVSAod2l0aCBmdWxsIGFscGhhIG9yIGZpeGVkIGFscGhhIHdoaWNoIGNvcnJlc3BvbmRzIHRvIHRoZSBhZGFwdGF0aW9uIHJhdGUgcGFyYW1ldGVyKQ0KDQpgYGB7cn0NCm1vZGVsLk9VIDwtIHBoeWxvcGFycyh0cmFpdF9kYXRhID0gbWlzc2luZywgdHJlZSA9IHBoeWxvX3RyYWl0cyRwaHksIG1vZGVsID0gIk9VIikNCg0KbW9kZWwubXZPVSA8LSBwaHlsb3BhcnModHJhaXRfZGF0YSA9IG1pc3NpbmcsIHRyZWUgPSBwaHlsb190cmFpdHMkcGh5LCBtb2RlbCA9ICJtdk9VIiwgZnVsbF9hbHBoYSA9IFRSVUUsIHVzZXpzY29yZXMgPSBGQUxTRSkgIyB0aGUgdXNlenNjb3JlcyA9IEYgaXMgYSBzaG9ydCB0ZXJtIHNvbHV0aW9uIHRvIGEgdmVyc2lvbiBpc3N1ZXMgb24gQ1JBTiwgc2VlIGhlcmUgaHR0cHM6Ly9naXRodWIuY29tL2VyaWNnb29sc2J5L1JwaHlsb3BhcnMvaXNzdWVzLzU0DQoNCm1vZGVsLm12T1VfZGlhZyA8LSBwaHlsb3BhcnModHJhaXRfZGF0YSA9IG1pc3NpbmcsIHRyZWUgPSBwaHlsb190cmFpdHMkcGh5LCBtb2RlbCA9ICJtdk9VIiwgZnVsbF9hbHBoYSA9IEZBTFNFLCB1c2V6c2NvcmVzID0gRkFMU0UpDQoNCm1vZGVsLkVCIDwtIHBoeWxvcGFycyh0cmFpdF9kYXRhID0gbWlzc2luZywgdHJlZSA9IHBoeWxvX3RyYWl0cyRwaHksIG1vZGVsID0gIkVCIikNCg0KbW9kZWwubGFtYmRhIDwtIHBoeWxvcGFycyh0cmFpdF9kYXRhID0gbWlzc2luZywgdHJlZSA9IHBoeWxvX3RyYWl0cyRwaHksIG1vZGVsID0gImxhbWJkYSIpDQpgYGANCg0KVGhpcyBjYW4gdGFrZSBxdWl0ZSBhIHdoaWxlIHRvIHJ1bi4NCg0KV2UgY2FuIGNvbXBhcmUgdGhlIEFJQ3Mgb2YgZWFjaCBtb2RlbC4NCg0KYGBge3J9DQphaWNzX21vZGVsczIgPC0gc2V0TmFtZXMoYyhBSUMobW9kZWwuQk0pLCBBSUMobW9kZWwuT1UpLCBBSUMobW9kZWwubXZPVSksIEFJQyhtb2RlbC5tdk9VX2RpYWcpLCBBSUMobW9kZWwuRUIpLCBBSUMobW9kZWwubGFtYmRhKSksIGMoIkJNIiwgIk9VIiwgIm12T1UiLCAibXZPVV9kaWFnIiwgIkVCIiwgImxhbWJkYSIpKQ0KDQphaWNzX21vZGVsczINCg0KYWljdyhhaWNzX21vZGVsczIpDQoNCmBgYA0KDQpIZXJlIGEgbGFtYmRhIHRyYW5zZm9ybWF0aW9uIGFwcGxpZWQgdG8gdGhlIHRyZWUgc2VlbXMgdG8gZml0IHRoZSBkYXRhIGJlc3QuDQoNCldlIGNhbiBub3cgZ2V0IHRoZSBpbXB1dGVkIHZhbHVlcyBmcm9tIHRoZSBtb2RlbCBhbmQgdXNlIHRoZSB2YXJpYW5jZSB0byBmaWd1cmUgb3V0IHRoZWlyIGxvd2VyIGFuZCB1cHBlciA5NSUgY29uZiBpbnRlcnZhbC4NCg0KYGBge3J9DQptb2RlbC5sYW1iZGEkYW5jX3JlY29uWzE6NSxdICU+JSBhc190aWJibGUoKSAjIG1lYW5zDQptb2RlbC5sYW1iZGEkYW5jX3ZhclsxOjUsXSAlPiUgYXNfdGliYmxlKCkgIyB2YXJpYW5jZXMNCm1vZGVsLmxhbWJkYSRhbmNfcmVjb25bMTo1LF0tIHNxcnQobW9kZWwubGFtYmRhJGFuY192YXJbMTo1LF0pKjEuOTYgIyBMb3dlciA5NSUgQ0kNCm1vZGVsLmxhbWJkYSRhbmNfcmVjb25bMTo1LF0rIHNxcnQobW9kZWwubGFtYmRhJGFuY192YXJbMTo1LF0pKjEuOTYgI1VwcGVyIDk1JSBDSQ0KYGBgDQoNCllvdSBjYW4gc2VlIHRoYXQgdGhlIHZhcmlhbmNlIGZvciBTZWVkbWFzcyBpcyBxdWl0ZSBoaWdoLiBGb3IgU29saWRhZ29fcmlnaWRhLCB0aGUgdHJhaXQgdmFsdWVzIGdvIGZyb20gLTIuNTcgdG8gMy4yNy4NCg0KYGBge3J9DQojIHZhcmlhbmNlIGluIFNlZWRtYXNzIG1pc3NpbmcgdmFsdWVzIGltcHV0ZWQgYnkgUnBoeWxvcGFycw0KbW9kZWwubGFtYmRhJGFuY192YXJbd2hpY2goaXMubmEobWlzc2luZyRTZWVkbWFzcykpLCJTZWVkbWFzcyJdICU+JSBzdW1tYXJ5KCkNCmBgYA0KV2UgY291bGQgdXNlIHRoaXMgdG8gZmlsdGVyIG91dCBpbXB1dGVkIHZhbHVlcyB0aGF0IGhhdmUgdG9vIGhpZ2ggYSB2YXJpYW5jZS4NCg0KDQojIyBQVlINCg0KTm93IHdlIHdpbGwgdXNlIHRoZSBQaHlsb2dlbmV0aWMgRWlnZW52ZWN0b3IgUmVncmVzc2lvbiB0ZWNobmlxdWUgKFBWUikuDQoNCmBgYHtyfQ0KcGh5bG8uY29yIDwtIGNvcGhlbmV0aWMucGh5bG8ocGh5bG9fdHJhaXRzJHBoeSkNCg0KIyBDaGVjayBpZiB0aGUgZGlzdGFuY2UgbWF0cml4IGlzIGV1Y2xpZGVhbiwgaWYgbm90LCBjYW4gc3F1YXJlIHJvb3QgdGhlIHBoeWxvZ2VueQ0KaXMuZXVjbGlkKGFzLmRpc3QocGh5bG8uY29yKSkNCg0KcGh5bG8ucGNvYSA8LSBkdWRpLnBjbyhkID0gYXMuZGlzdChwaHlsby5jb3IpLCBzY2FubmYgPSBGLCBmdWxsID0gVCkNCiMgZnJvbSBhcGUsIGNhbGN1bGF0ZXMgYnJva2VuIHN0aWNrIGF1dG9tYXRpY2FsbHkNCnBoeWxvLnBjb2EyIDwtIHBjb2EoRCA9IGFzLmRpc3QocGh5bG8uY29yKSkNCmBgYA0KDQpPayBub3cgd2UgaGF2ZSBvdXIgcGh5bG9nZW5ldGljIGVpZ2VuZXZlY3RvcnMsIHRoZSBxdWVzdGlvbiBpcyBob3cgbWFueSBkbyB3ZSBzZWxlY3QgdG8gaW5jbHVkZSBpbnRvIG91ciByZWdyZXNzaW9uPw0KDQpTb21lIHBlb3BsZSBzYXkgYWxsLCBzb21lIHBlb3BsZSBzYXkgdXNlIGEgYnJva2VuIHN0aWNrIG1vZGVsLCBzb21lIHBlb3BsZSBzYXkgdXNlIHRob3NlIHRoYXQgZXhwbGFpbiBhdCBsZWFzdCA1MCUgb2YgdmFyaWFuY2UuIFNlZSBkaXNjdXNzaW9uIGJ5IFJvbGhmICwgMjAwMSBhbmQgTWFydGlucyBldCBhbC4gMjAwMi4NCg0KRm9yIG5vdyBJIHdpbGwgdXNlIHRoZSBicm9rZW4gc3RpY2sgbW9kZWwuIFNvbWUgaGF2ZSBzdWdnZXN0ZWQgYGZhLnBhcmFsbGVsYCBmcm9tIHBhY2thZ2UgYHBzeWNoYCBidXQgSSBoYXZlIGhhZCBtYW55IGlzc3VlcyB3aXRoIHRoaXMuDQpXZSBjYW4gY2hlY2sgdGhpcyBvdXQgdmlzdWFsbHkgYW5kIGV4dHJhY3QgdGhlc2UgZnJvbSB0aGUgb2JqZWN0DQoNCmBgYHtyfQ0KZ2dwbG90KCkgKyBnZW9tX3BvaW50KGRhdGE9cGh5bG8ucGNvYTIkdmFsdWVzLCBhZXMoeD0xOm5yb3cocGh5bG8ucGNvYTIkdmFsdWVzKSwgeT1SZWxhdGl2ZV9laWcpLCBjb2w9ImJsdWUiKSArIGdlb21fcG9pbnQoZGF0YT1waHlsby5wY29hMiR2YWx1ZXMsIGFlcyh4PTE6bnJvdyhwaHlsby5wY29hMiR2YWx1ZXMpLCB5PUJyb2tlbl9zdGljayksIGNvbD0icmVkIikNCg0KKHNpZy5FViA8LXBoeWxvLnBjb2EyJHZhbHVlc1t3aGljaChwaHlsby5wY29hMiR2YWx1ZXMkUmVsYXRpdmVfZWlnID4gcGh5bG8ucGNvYTIkdmFsdWVzJEJyb2tlbl9zdGljayksXSkNCmBgYA0KDQoNCg0KYGBge3J9DQojIFRha2UgdGhlIGVpZ2VudmVjdG9ycyBhbmQgYWRkIHRoZW0gdG8gdGhlIHRyYWl0cyBtYXRyaXgNCg0KbWlzc2luZ19waHlsbyA8LSBjYmluZChtaXNzaW5nLCBwaHlsby5wY29hJGxpWywxOm5yb3coc2lnLkVWKV0pICU+JSBkcGx5cjo6c2VsZWN0KC1zcGVjaWVzKQ0KDQpwdnIgPC0gbWlzc0ZvcmVzdCh4bWlzID0gbWlzc2luZ19waHlsbywgbnRyZWUgPSAxMDAwLCBtYXhpdGVyID0gMTAwLCB2YXJpYWJsZXdpc2UgPSBUKQ0KYGBgDQoNCkNoZWNrIHRoZSBvdXRwdXQNCg0KYGBge3J9DQpoZWFkKHB2cikNCmBgYA0KDQpXZSBjYW4gc2VlIHRoYXQgdGhlIE91dCBPZiBCYWcgRXJyb3IgKE9PQikgaXMgbXVjaCBoaWdoZXIgZm9yIHRyYWl0IDYgKGllLiBTZWVkIE1hc3MpIHdpdGggYW4gT09CIG9mIDMuMDMgY29tcGFyZWQgdG8gb3RoZXJzLg0KDQpDb21wYXJlIHRoZSByZXN1bHRzIG9mIHRoZSB0cnVlIG1hdHJpeCwgdGhlIHJQaHlsb3BhcnMgYXBwcm9hY2ggYW5kIHRoZSBtaXNzRm9yZXN0IGFwcHJvYWNoDQoNCmBgYHtyfQ0KcGh5bG9fdHJhaXRzJGRhdGFbLC0xXQ0KcHZyJHhpbXANCmRhdGEuZnJhbWUobW9kZWwubGFtYmRhJGFuY19yZWNvblsxOjIwMCxdKQ0KYGBgDQoNCldlIGNhbiBjaGVjayBob3cgd2VsbCB0aGlzIGhhcyBkb25lIGZvciBlYWNoIHZhcmlhYmxlDQoNCmBgYHtyfQ0KIyBMTUENCm1pc3NpbmcuTE1BIDwtIHdoaWNoKGlzLm5hKG1pc3NpbmckTE1BKSkNCg0KY29yKHBoeWxvX3RyYWl0cyRkYXRhW21pc3NpbmcuTE1BLCJMTUEiXSwgcHZyJHhpbXBbbWlzc2luZy5MTUEsIkxNQSJdKSAlPiUgcm91bmQoMikgJT4lIHBhc3RlKCJjb3IiLC4sIlBWUiIpDQoNCmNvcihwaHlsb190cmFpdHMkZGF0YVttaXNzaW5nLkxNQSwiTE1BIl0sIG1vZGVsLmxhbWJkYSRhbmNfcmVjb25bbWlzc2luZy5MTUEsIkxNQSJdKSAlPiUgcm91bmQoMikgJT4lIHBhc3RlKCJjb3IiLC4sInBHTE0iKQ0KYGBgDQoNCmBgYHtyfQ0KI0xBDQptaXNzaW5nLkxBIDwtIHdoaWNoKGlzLm5hKG1pc3NpbmckTEEpKQ0KDQpjb3IocGh5bG9fdHJhaXRzJGRhdGFbbWlzc2luZy5MQSwiTEEiXSwgcHZyJHhpbXBbbWlzc2luZy5MQSwiTEEiXSkgJT4lIHJvdW5kKDIpICU+JSBwYXN0ZSgiY29yIiwuLCJQVlIiKQ0KDQpjb3IocGh5bG9fdHJhaXRzJGRhdGFbbWlzc2luZy5MQSwiTEEiXSwgbW9kZWwubGFtYmRhJGFuY19yZWNvblttaXNzaW5nLkxBLCJMQSJdKSAlPiUgcm91bmQoMikgJT4lIHBhc3RlKCJjb3IiLC4sInBHTE0iKQ0KYGBgDQpOb3QgZ3JlYXQgaGVyZSBmb3IgYm90aCBvZiB0aGVtLiBwR0xNIGlzIG11Y2ggYmV0dGVyIHRob3VnaC4NCg0KYGBge3J9DQojTm1hc3MNCm1pc3NpbmcuTm1hc3MgPC0gd2hpY2goaXMubmEobWlzc2luZyRObWFzcykpDQoNCmNvcihwaHlsb190cmFpdHMkZGF0YVttaXNzaW5nLk5tYXNzLCJObWFzcyJdLCBwdnIkeGltcFttaXNzaW5nLk5tYXNzLCJObWFzcyJdKSAlPiUgcm91bmQoMikgJT4lIHBhc3RlKCJjb3IiLC4sIlBWUiIpDQoNCmNvcihwaHlsb190cmFpdHMkZGF0YVttaXNzaW5nLk5tYXNzLCJObWFzcyJdLCBtb2RlbC5sYW1iZGEkYW5jX3JlY29uW21pc3NpbmcuTm1hc3MsIk5tYXNzIl0pICU+JSByb3VuZCgyKSAlPiUgcGFzdGUoImNvciIsLiwicEdMTSIpDQpgYGANCg0KYGBge3J9DQojU1NEDQptaXNzaW5nLlNTRCA8LSB3aGljaChpcy5uYShtaXNzaW5nJFNTRCkpDQoNCmNvcihwaHlsb190cmFpdHMkZGF0YVttaXNzaW5nLlNTRCwiU1NEIl0sIHB2ciR4aW1wW21pc3NpbmcuU1NELCJTU0QiXSkgJT4lIHJvdW5kKDIpICU+JSBwYXN0ZSgiY29yIiwuLCJQVlIiKQ0KDQpjb3IocGh5bG9fdHJhaXRzJGRhdGFbbWlzc2luZy5TU0QsIlNTRCJdLCBtb2RlbC5sYW1iZGEkYW5jX3JlY29uW21pc3NpbmcuU1NELCJTU0QiXSkgJT4lIHJvdW5kKDIpICU+JSBwYXN0ZSgiY29yIiwuLCJwR0xNIikNCmBgYA0KDQpgYGB7cn0NCiNTZWVkbWFzcw0KbWlzc2luZy5TZWVkbWFzcyA8LSB3aGljaChpcy5uYShtaXNzaW5nJFNlZWRtYXNzKSkNCg0KY29yKHBoeWxvX3RyYWl0cyRkYXRhW21pc3NpbmcuU2VlZG1hc3MsIlNlZWRtYXNzIl0sIHB2ciR4aW1wW21pc3NpbmcuU2VlZG1hc3MsIlNlZWRtYXNzIl0pICU+JSByb3VuZCgyKSAlPiUgcGFzdGUoImNvciIsLiwiUFZSIikNCg0KY29yKHBoeWxvX3RyYWl0cyRkYXRhW21pc3NpbmcuU2VlZG1hc3MsIlNlZWRtYXNzIl0sIG1vZGVsLmxhbWJkYSRhbmNfcmVjb25bbWlzc2luZy5TZWVkbWFzcywiU2VlZG1hc3MiXSkgJT4lIHJvdW5kKDIpICU+JSBwYXN0ZSgiY29yIiwuLCJwR0xNIikNCmBgYA0KDQpBbmQgaGVpZ2h0DQoNCmBgYHtyfQ0KI0hlaWdodA0KbWlzc2luZy5IZWlnaHQgPC0gd2hpY2goaXMubmEobWlzc2luZyRIZWlnaHQpKQ0KDQpjb3IocGh5bG9fdHJhaXRzJGRhdGFbbWlzc2luZy5IZWlnaHQsIkhlaWdodCJdLCBwdnIkeGltcFttaXNzaW5nLkhlaWdodCwiSGVpZ2h0Il0pICU+JSByb3VuZCgyKSAlPiUgcGFzdGUoImNvciIsLiwiUFZSIikNCg0KY29yKHBoeWxvX3RyYWl0cyRkYXRhW21pc3NpbmcuSGVpZ2h0LCJIZWlnaHQiXSwgbW9kZWwubGFtYmRhJGFuY19yZWNvblttaXNzaW5nLkhlaWdodCwiSGVpZ2h0Il0pICU+JSByb3VuZCgyKSAlPiUgcGFzdGUoImNvciIsLiwicEdMTSIpDQpgYGANCg0KT2sgc28gb3ZlcmFsbCBpdCdzIG5vdCB0b28gYmFkISBCb3RoIG1ldGhvZHMgYXJlIHZlcnkgY29tcGFyYWJsZSBhbmQgb25seSBMZWFmIEFyZWEgYW5kIE5pdHJvZ2VuIE1hc3MgYXJlIHBvb3JseSByZWNvbnN0cnVjdGVkLiANCg0KV2hhdCBoYXBwZW5zIGlmIHdlIHRyeSBhbmQgaW1wdXRlIHRoZXNlIHdpdGhvdXQgcGh5bG9nZW5ldGljIGluZm9ybWF0aW9uPw0KDQpgYGB7cn0NCmltcHV0ZS5ub3BoeWxvIDwtIG1pc3NGb3Jlc3QoeG1pcyA9IG1pc3NpbmdbLC0xXSwgbnRyZWUgPSAxMDAwLCBtYXhpdGVyID0gMTAwLCB2YXJpYWJsZXdpc2UgPSBUKQ0KY29yKHBoeWxvX3RyYWl0cyRkYXRhW21pc3NpbmcuTEEsIkxBIl0sIGltcHV0ZS5ub3BoeWxvJHhpbXBbbWlzc2luZy5MQSwiTEEiXSkNCmNvcihwaHlsb190cmFpdHMkZGF0YVttaXNzaW5nLk5tYXNzLCJObWFzcyJdLCBpbXB1dGUubm9waHlsbyR4aW1wW21pc3NpbmcuTm1hc3MsIk5tYXNzIl0pDQoNCmBgYA0KDQoNCk9rIHNvIGZvciBgTGVhZiBBcmVhYCwgaXQncyBhIHRhZCB3b3JzZSB0aGFuIHdoZW4gdXNpbmcgcGh5bG9nZW55LCBmb3IgYE5tYXNzYCBpdCdzIHRoZSBzYW1lLiBUaGlzIGlzIGJlY2F1c2UgYExlYWYgQXJlYWAgZG9lc24ndCBoYXZlIHN1cGVyIHN0cm9uZyBwaHlsb2dlbmV0aWMgc2lnbmFsIGFuZCBgTm1hc3NgIGhhcyBubyBwaHlsb2dlbmV0aWMgc2lnbmFsIGF0IGFsbCBhbmQgaXMgYmVzdCBmaXR0ZWQgYnkgYSB3aGl0ZS1ub2lzZSBtb2RlbC4NCg0KSXQgaXMgYWxzbyBwb3NzaWJsZSB0aGF0IHRoZSBmaXJzdCBwaHlsb2dlbmV0aWMgZWlnZW52ZWN0b3JzIHRoYXQgd2Ugc2VsZWN0ZWQgYXJlbid0IHRoZSBvbmVzIHRoYXQgYmVzdCBzdWl0IGBMZWFmIEFyZWFgLCBzbyB0aGUgUFZSIG1ldGhvZCBkb2VzIHBvb3JseS4NCg0KDQpXZSBjYW4gY2hlY2sgdGhlIG91dCBvZiBiYWcgZXJyb3INCg0KYGBge3J9DQpyYmluZChwdnIkT09CZXJyb3JbMTo2XSwgaW1wdXRlLm5vcGh5bG8kT09CZXJyb3IpICU+JSBgY29sbmFtZXM8LWAobmFtZXMoaW1wdXRlLm5vcGh5bG8keGltcCkpICU+JSBgcm93bmFtZXM8LWAoYygiUkZfUFZSIiwgIlJGX25vX3BoeWxvIikpDQpgYGANCg0KSW50ZXJlc3RpbmcsIHRoZSBPT0IgZXJyb3IgZm9yIE5NYXNzIGlzIHF1aXRlIGxvdyBjb21wYXJlZCB0byBvdGhlcnMsIGJ1dCB0aGUgY29ycmVsYXRpb24gb2YgdGhlIGltcHV0ZWQgdmFsdWVzIGlzIGJhZC4NCg0KTGV0J3MgcXVpY2tseSBjaGVjayB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB0cmFpdHMNCg0KYGBge3J9DQpjb3IocGh5bG9fdHJhaXRzJGRhdGFbLC0xXSkNCmBgYA0KDQoNCiMjIFBFTQ0KDQpUaGlzIHBhcnQgaXMgdmVyeSBtdWNoIGV4cGVyaW1lbnRhbCwgYW5kIEkgZG9uJ3QgcXVpdGUgaGF2ZSBhIHN0cm9uZyBncmlwIG9uIGl0IHlldC4NCg0KYGBge3J9DQpsaWJyYXJ5KE1QU0VNKQ0KdHJlZS5wZ3JhcGggPC0gUGh5bG8yRGlyZWN0ZWRHcmFwaChwaHlsb190cmFpdHMkcGh5KQ0KYGBgDQoNCmBgYHtyfQ0KdHJlZS5QRU0gPC1QRU0uYnVpbGQodHJlZS5wZ3JhcGgsIGQ9ImRpc3RhbmNlIixzcD0ic3BlY2llcyIpICMgaGVyZSBJJ20gbm90IGFkZGluZyBhbnkgYSBvciBwc2kgcGFyYW1ldGVycw0KYXMuZGF0YS5mcmFtZSh0cmVlLlBFTSkNCmBgYA0KDQpUaGlzIG5leHQgc29sdXRpb24gcmVxdWlyZXMgY29tcGxldGUgdHJhaXQgdmFsdWVzLCBubyBOQXMuDQoNCmBgYHtyfQ0KIyBoZXJlIHdlIGNhbiBsZXQgdGhlIGRhdGEgc3BlYWsgZm9yIGl0c2VsZg0KIyB1c2UgUk1MRSB0byBlc3RpbWF0ZSBhIA0KdHJlZS5QRU1fb3B0MiA8LSBQRU0uZml0U2ltcGxlKA0KeSA9IGFzLm1hdHJpeChwaHlsb190cmFpdHMkZGF0YVssLTFdKSwNCng9TlVMTCwNCncgPSB0cmVlLnBncmFwaCwNCmQgPSAiZGlzdGFuY2UiLCBzcD0ic3BlY2llcyIsDQpsb3dlciA9IDAsIHVwcGVyID0gMSkNCg0KYGBgDQoNClRoZSBwYWNrYWdlIHVzZXMgYSBsaW5lYXIgbW9kZWwgdmlhIHN0ZXB3aXNlIHNlbGVjdGlvbiBiYXNlZCBvbiBBSUMgY3JpdGVyaW9uLiBJdCBjYW4gb25seSBkbyBpdCBmb3Igb25lIHRyYWl0IGF0IGEgdGltZSBhbmQgY2FuIGFjY2VwdCBhIGNlcnRhaW4gYW1vdW50IG9mIE5BIHZhbHVlcy4NCg0KYGBge3J9DQojIElzIHZlcnkgc2xvdyBmb3IgbGFyZ2UgZGF0YXNldHMNCiMjbG0yIDwtIGxtZm9yd2FyZHNlcXVlbnRpYWxBSUNjKHkgPSBtaXNzaW5nWywyXSwgb2JqZWN0ID0gdHJlZS5QRU1fb3B0MikNCiNzdW1tYXJ5KGxtMikNCmBgYA0KDQpgYGB7cn0NCiMjIEVzdGltYXRpbmcgYW5jZXN0cmFsIHRyYWl0IHZhbHVlczoNCkFOQ2xvYyA8LSBnZXRBbmNHcmFwaExvY2F0aW9ucyh0cmVlLnBncmFwaCkNClBFTWZzQW5jIDwtIFBFTS5maXRTaW1wbGUoeT1hcy5tYXRyaXgocGh5bG9fdHJhaXRzJGRhdGFbLC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHg9TlVMTCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgdz1BTkNsb2MkeCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZD0iZGlzdGFuY2UiLHNwPSJzcGVjaWVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbG93ZXI9MCx1cHBlcj0xKQ0KYGBgDQoNCmBgYHtyfQ0KUEVNYW5jMSA8LSBMb2NhdGlvbnMyUEVNc2NvcmVzKFBFTWZzQW5jLCBBTkNsb2MpDQojIHRoZXJlIGlzIGFub3RoZXIgc3RlcCB0byB0aGlzIHNlY3Rpb24gdGhhdCdzIGkgY2FuJ3QgZmlndXJlIGhvdyB0byB3b3JrIG91dCB5ZXQNCmBgYA0KDQoNCk5vdyB1c2UgdGhlc2UgaW4gdGhlIGltcHV0YXRpb24gbWV0aG9kIHdpdGggUkYuDQoNCmBgYHtyfQ0KbWlzc2luZ19waHlsbyA8LSBjYmluZChtaXNzaW5nLCBhcy5kYXRhLmZyYW1lKHRyZWUuUEVNX29wdDIpWywxOm5jb2woc2lnLkVWKV0pICU+JSBkcGx5cjo6c2VsZWN0KC1zcGVjaWVzKQ0KDQpwZW0gPC0gbWlzc0ZvcmVzdCh4bWlzID0gbWlzc2luZ19waHlsbywgbnRyZWUgPSAxMDAwLCBtYXhpdGVyID0gMTAwLCB2YXJpYWJsZXdpc2UgPSBUKQ0KYGBgDQoNCkNoZWNrDQoNCmBgYHtyfQ0KIyBMTUENCm1pc3NpbmcuTE1BIDwtIHdoaWNoKGlzLm5hKG1pc3NpbmckTE1BKSkNCg0KY29yKHBoeWxvX3RyYWl0cyRkYXRhW21pc3NpbmcuTE1BLCJMTUEiXSwgcHZyJHhpbXBbbWlzc2luZy5MTUEsIkxNQSJdKSAlPiUgcm91bmQoMikgJT4lIHBhc3RlKCJjb3IiLC4sIlBWUiIpDQoNCmNvcihwaHlsb190cmFpdHMkZGF0YVttaXNzaW5nLkxNQSwiTE1BIl0sIG1vZGVsLmxhbWJkYSRhbmNfcmVjb25bbWlzc2luZy5MTUEsIkxNQSJdKSAlPiUgcm91bmQoMikgJT4lIHBhc3RlKCJjb3IiLC4sInBHTE0iKQ0KDQpjb3IocGh5bG9fdHJhaXRzJGRhdGFbbWlzc2luZy5MTUEsIkxNQSJdLCBwZW0keGltcFttaXNzaW5nLkxNQSwiTE1BIl0pICU+JSByb3VuZCgyKSAlPiUgcGFzdGUoImNvciIsLiwiUEVNIikNCmBgYA0KDQoNCmBgYHtyfQ0KI0xBDQptaXNzaW5nLkxBIDwtIHdoaWNoKGlzLm5hKG1pc3NpbmckTEEpKQ0KDQpjb3IocGh5bG9fdHJhaXRzJGRhdGFbbWlzc2luZy5MQSwiTEEiXSwgcHZyJHhpbXBbbWlzc2luZy5MQSwiTEEiXSkgJT4lIHJvdW5kKDIpICU+JSBwYXN0ZSgiY29yIiwuLCJQVlIiKQ0KDQpjb3IocGh5bG9fdHJhaXRzJGRhdGFbbWlzc2luZy5MQSwiTEEiXSwgbW9kZWwubGFtYmRhJGFuY19yZWNvblttaXNzaW5nLkxBLCJMQSJdKSAlPiUgcm91bmQoMikgJT4lIHBhc3RlKCJjb3IiLC4sInBHTE0iKQ0KDQpjb3IocGh5bG9fdHJhaXRzJGRhdGFbbWlzc2luZy5MQSwiTEEiXSwgcGVtJHhpbXBbbWlzc2luZy5MQSwiTEEiXSkgJT4lIHJvdW5kKDIpICU+JSBwYXN0ZSgiY29yIiwuLCJQRU0iKQ0KYGBgDQoNCg0KYGBge3J9DQojTm1hc3MNCm1pc3NpbmcuTm1hc3MgPC0gd2hpY2goaXMubmEobWlzc2luZyRObWFzcykpDQoNCmNvcihwaHlsb190cmFpdHMkZGF0YVttaXNzaW5nLk5tYXNzLCJObWFzcyJdLCBwdnIkeGltcFttaXNzaW5nLk5tYXNzLCJObWFzcyJdKSAlPiUgcm91bmQoMikgJT4lIHBhc3RlKCJjb3IiLC4sIlBWUiIpDQoNCmNvcihwaHlsb190cmFpdHMkZGF0YVttaXNzaW5nLk5tYXNzLCJObWFzcyJdLCBtb2RlbC5sYW1iZGEkYW5jX3JlY29uW21pc3NpbmcuTm1hc3MsIk5tYXNzIl0pICU+JSByb3VuZCgyKSAlPiUgcGFzdGUoImNvciIsLiwicEdMTSIpDQoNCmNvcihwaHlsb190cmFpdHMkZGF0YVttaXNzaW5nLk5tYXNzLCJObWFzcyJdLCBwZW0keGltcFttaXNzaW5nLk5tYXNzLCJObWFzcyJdKSAlPiUgcm91bmQoMikgJT4lIHBhc3RlKCJjb3IiLC4sIlBFTSIpDQpgYGANCg0KU28gdmVyeSBzaW1pbGFyIHRvIFBWUiBpZiBub3Qgd29yc2UsIG5vdCBtdWNoIG9mIGFuIGltcHJvdmVtZW50IHJlYWxseSAuLi4gVW5sZXNzIEknbSBkb2luZyB0aGlzIHdyb25nPw0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIENvbWJpbmluZyB0aGUgYmVzdCBvZiBib3RoIHdvcmxkcw0KDQpPbmUgc29sdXRpb24gaXMgdG8gcnVuIHNldmVyYWwgcm91bmRzIG9mIGltcHV0YXRpb24uDQoNCk9uZSBjb3VsZCB1c2UgUnBoeWxvcGFycyB0byBmaXJzdCBpbXB1dGUgdGhlIGNvbnRpbnVvdXMgdmFyaWFibGVzIHRoYXQgaGF2ZSBzdHJvbmcgcGh5bG9nZW5ldGljIHNpZ25hbCBhbmQgdGhlbiBydW4gcGh5bG9nZW5ldGljIGVpZ2VudmVjdG9ycyB3aXRoIFJGIHRvIGltcHV0ZSB0aGUgcmVtYWluaW5nIGNhdGVnb3JpY2FsIHRyYWl0cyBhbmQgdHJhaXRzIHRoYXQgaGF2ZSBsb3cgcGh5bG9nZW5ldGljIHNpZ25hbC4NCg0KTGV0J3MgcmUtcnVuIHRoZSBsYW1iZGEgbW9kZWwgYnV0IHJlbW92ZSBOTWFzcy4NClRoZW4gd2Ugd2lsbCBhZGQgTm1hc3MgYW5kIHRoZSBwaHlsb2dlbmV0aWMgZWlnZW52ZWN0b3JzLg0KDQpgYGB7cn0NCm1vZGVsLmxhbWJkYSRhbmNfcmVjb24gJT4lIGRpbSgpDQpgYGANCg0KYGBge3J9DQptb2RlbC5sYW1iZGEyIDwtIHBoeWxvcGFycyh0cmFpdF9kYXRhID0gbWlzc2luZyAlPiUgZHBseXI6OnNlbGVjdCgtTm1hc3MpICwgdHJlZSA9IHBoeWxvX3RyYWl0cyRwaHksIG1vZGVsID0gImxhbWJkYSIpDQpgYGANCg0KTm93IGFkZCB0aGUgTm1hc3MNCg0KYGBge3J9DQpjb21iLm1vZGVsIDwtIGRhdGEuZnJhbWUobW9kZWwubGFtYmRhMiRhbmNfcmVjb25bMTo1MDAsXSwgTm1hc3M9bWlzc2luZyRObWFzcywgcGh5bG8ucGNvYSRsaVssMTpucm93KHNpZy5FVildKQ0KDQpwdnIuY29tYiA8LSBtaXNzRm9yZXN0KHhtaXMgPSBjb21iLm1vZGVsLCBudHJlZSA9IDEwMDAsIG1heGl0ZXIgPSAxMDAsIHZhcmlhYmxld2lzZSA9IFQpDQpgYGANCg0KQW5kIG5vdyBleHRyYWN0IHRoZXNlIGFuZCBjb21wYXJlDQoNCmBgYHtyfQ0KIyBMTUENCm1pc3NpbmcuTE1BIDwtIHdoaWNoKGlzLm5hKG1pc3NpbmckTE1BKSkNCg0KY29yKHBoeWxvX3RyYWl0cyRkYXRhW21pc3NpbmcuTE1BLCJMTUEiXSwgcHZyJHhpbXBbbWlzc2luZy5MTUEsIkxNQSJdKSAlPiUgcm91bmQoMikgJT4lIHBhc3RlKCJjb3IiLC4sIlBWUiIpDQoNCmNvcihwaHlsb190cmFpdHMkZGF0YVttaXNzaW5nLkxNQSwiTE1BIl0sIG1vZGVsLmxhbWJkYSRhbmNfcmVjb25bbWlzc2luZy5MTUEsIkxNQSJdKSAlPiUgcm91bmQoMikgJT4lIHBhc3RlKCJjb3IiLC4sInBHTE0iKQ0KDQpjb3IocGh5bG9fdHJhaXRzJGRhdGFbbWlzc2luZy5MTUEsIkxNQSJdLCBwZW0keGltcFttaXNzaW5nLkxNQSwiTE1BIl0pICU+JSByb3VuZCgyKSAlPiUgcGFzdGUoImNvciIsLiwiUEVNIikNCg0KY29yKHBoeWxvX3RyYWl0cyRkYXRhW21pc3NpbmcuTE1BLCJMTUEiXSwgcHZyLmNvbWIkeGltcFttaXNzaW5nLkxNQSwiTE1BIl0pICU+JSByb3VuZCgyKSAlPiUgcGFzdGUoImNvciIsLiwiY29tYmluZWQiKQ0KYGBgDQoNCg0KTm1hc3MNCg0KYGBge3J9DQojTm1hc3MNCm1pc3NpbmcuTm1hc3MgPC0gd2hpY2goaXMubmEobWlzc2luZyRObWFzcykpDQoNCmNvcihwaHlsb190cmFpdHMkZGF0YVttaXNzaW5nLk5tYXNzLCJObWFzcyJdLCBwdnIkeGltcFttaXNzaW5nLk5tYXNzLCJObWFzcyJdKSAlPiUgcm91bmQoMikgJT4lIHBhc3RlKCJjb3IiLC4sIlBWUiIpDQoNCmNvcihwaHlsb190cmFpdHMkZGF0YVttaXNzaW5nLk5tYXNzLCJObWFzcyJdLCBtb2RlbC5sYW1iZGEkYW5jX3JlY29uW21pc3NpbmcuTm1hc3MsIk5tYXNzIl0pICU+JSByb3VuZCgyKSAlPiUgcGFzdGUoImNvciIsLiwicEdMTSIpDQoNCmNvcihwaHlsb190cmFpdHMkZGF0YVttaXNzaW5nLk5tYXNzLCJObWFzcyJdLCBwZW0keGltcFttaXNzaW5nLk5tYXNzLCJObWFzcyJdKSAlPiUgcm91bmQoMikgJT4lIHBhc3RlKCJjb3IiLC4sIlBFTSIpDQoNCmNvcihwaHlsb190cmFpdHMkZGF0YVttaXNzaW5nLk5tYXNzLCJObWFzcyJdLCBwdnIuY29tYiR4aW1wW21pc3NpbmcuTm1hc3MsIk5tYXNzIl0pICU+JSByb3VuZCgyKSAlPiUgcGFzdGUoImNvciIsLiwiY29tYmluZWQiKQ0KYGBgDQoNCkludGVyZXN0aW5nLCBpdCBzZWVtcyBsaWtlIHRoZSBjb21iaW5lZCBtZXRob2QgaXMgYWN0dWFsbHkgd29yc2UgdGhhbiBhbnkgbWV0aG9kIQ0KDQpXaGF0IGlmIHdlIGluY2x1ZGUgTm1hc3MgaW4gdGhlIG9yaWdpbmFsIGltcHV0YXRpb24sIGJ1dCB0aGVuIHJlbW92ZSB0aG9zZSByZXN1bHRzIGFuZCByZS1ydW4gdGhlIHJhbmRvbUZvcmVzdCA/DQoNCg0KYGBge3J9DQptb2RlbC5sYW1iZGEzIDwtIHBoeWxvcGFycyh0cmFpdF9kYXRhID0gbWlzc2luZyAsIHRyZWUgPSBwaHlsb190cmFpdHMkcGh5LCBtb2RlbCA9ICJsYW1iZGEiKQ0KY29tYi5tb2RlbDIgPC0gZGF0YS5mcmFtZShtb2RlbC5sYW1iZGEzJGFuY19yZWNvblsxOjUwMCwtMl0sIE5tYXNzPW1pc3NpbmckTm1hc3MsIHBoeWxvLnBjb2EkbGlbLDE6bnJvdyhzaWcuRVYpXSkNCnB2ci5jb21iMiA8LSBtaXNzRm9yZXN0KHhtaXMgPSBjb21iLm1vZGVsMiwgbnRyZWUgPSAxMDAwLCBtYXhpdGVyID0gMTAwLCB2YXJpYWJsZXdpc2UgPSBUKQ0KDQojTE1BDQpjb3IocGh5bG9fdHJhaXRzJGRhdGFbbWlzc2luZy5MTUEsIkxNQSJdLCBwdnIkeGltcFttaXNzaW5nLkxNQSwiTE1BIl0pICU+JSByb3VuZCgyKSAlPiUgcGFzdGUoImNvciIsLiwiUFZSIikNCg0KY29yKHBoeWxvX3RyYWl0cyRkYXRhW21pc3NpbmcuTE1BLCJMTUEiXSwgbW9kZWwubGFtYmRhJGFuY19yZWNvblttaXNzaW5nLkxNQSwiTE1BIl0pICU+JSByb3VuZCgyKSAlPiUgcGFzdGUoImNvciIsLiwicEdMTSIpDQoNCmNvcihwaHlsb190cmFpdHMkZGF0YVttaXNzaW5nLkxNQSwiTE1BIl0sIHBlbSR4aW1wW21pc3NpbmcuTE1BLCJMTUEiXSkgJT4lIHJvdW5kKDIpICU+JSBwYXN0ZSgiY29yIiwuLCJQRU0iKQ0KDQpjb3IocGh5bG9fdHJhaXRzJGRhdGFbbWlzc2luZy5MTUEsIkxNQSJdLCBwdnIuY29tYjIkeGltcFttaXNzaW5nLkxNQSwiTE1BIl0pICU+JSByb3VuZCgyKSAlPiUgcGFzdGUoImNvciIsLiwiY29tYmluZWQiKQ0KYGBgDQoNCg0KYGBge3J9DQojTm1hc3MNCg0KY29yKHBoeWxvX3RyYWl0cyRkYXRhW21pc3NpbmcuTm1hc3MsIk5tYXNzIl0sIHB2ciR4aW1wW21pc3NpbmcuTm1hc3MsIk5tYXNzIl0pICU+JSByb3VuZCgyKSAlPiUgcGFzdGUoImNvciIsLiwiUFZSIikNCg0KY29yKHBoeWxvX3RyYWl0cyRkYXRhW21pc3NpbmcuTm1hc3MsIk5tYXNzIl0sIG1vZGVsLmxhbWJkYSRhbmNfcmVjb25bbWlzc2luZy5ObWFzcywiTm1hc3MiXSkgJT4lIHJvdW5kKDIpICU+JSBwYXN0ZSgiY29yIiwuLCJwR0xNIikNCg0KY29yKHBoeWxvX3RyYWl0cyRkYXRhW21pc3NpbmcuTm1hc3MsIk5tYXNzIl0sIHBlbSR4aW1wW21pc3NpbmcuTm1hc3MsIk5tYXNzIl0pICU+JSByb3VuZCgyKSAlPiUgcGFzdGUoImNvciIsLiwiUEVNIikNCg0KY29yKHBoeWxvX3RyYWl0cyRkYXRhW21pc3NpbmcuTm1hc3MsIk5tYXNzIl0sIHB2ci5jb21iMiR4aW1wW21pc3NpbmcuTm1hc3MsIk5tYXNzIl0pICU+JSByb3VuZCgyKSAlPiUgcGFzdGUoImNvciIsLiwiY29tYmluZWQiKQ0KYGBgDQoNCk9rLCBzbyBldmVuIGhlcmUgaXQncyBub3QgYXMgZ29vZC4gaW50ZXJlc3RpbmcuDQoNClBlcmhhcHMgdGhpcyBpcyBiZXN0IGRvbmUgb25seSB3aXRoIHJlbWFpbmluZyBjYXRlZ29yaWNhbCB0cmFpdHMuDQoNCiMgQkhQTUYNCg0KRmluYWwgdGVzdA0KDQpgYGB7cn0NCmxpYnJhcnkoQkhQTUYpDQp0bXAuZGlyIDwtIGRpcm5hbWUoIi4vdG1wIikNCmRhdGEoImhpZXJhcmNoeS5pbmZvIikNCmRhdGEoInRyYWl0LmluZm8iKQ0KYGBgDQoNCmBgYHtyfQ0KaGllcmFyY2h5LmluZm8gPC0gZGF0YS5mcmFtZShwbGFudF9pZCA9IDE6NTAwLCBzcGVjaWVzPSByb3duYW1lcyhtaXNzaW5nKSwgZ2VudXMgPSAsIGZhbWlseSA9ICwgb3JkZXIgPSApDQpgYGANCg0KDQpgYGB7cn0NCkJIUE1GLnRlc3QgPC0gR2FwRmlsbGluZyh0cmFpdC5pbmZvLCBoaWVyYXJjaHkuaW5mbywNCiAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuLmdhcC5maWxsZWQub3V0cHV0LnBhdGggPSBwYXN0ZTAodG1wLmRpciwgIi9tZWFuX2dhcF9maWxsZWQudHh0IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgc3RkLmdhcC5maWxsZWQub3V0cHV0LnBhdGggPSBwYXN0ZTAodG1wLmRpciwgIi9zdGRfZ2FwX2ZpbGxlZC50eHQiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICB0bXAuZGlyID0gdG1wLmRpcikNCmBgYA0KYGBge3J9DQojI291dDEgPC0gQ2FsY3VsYXRlQ3ZSbXNlKHRyYWl0LmluZm8sIGhpZXJhcmNoeS5pbmZvKQ0KYGBgDQoNCg0K